mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-07-01 18:30:15 +00:00
Compare commits
130 Commits
release-ky
...
removeRepl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c45e05b7bd | ||
|
|
76bae738a0 | ||
|
|
0770661b2a | ||
|
|
67d5871e87 | ||
|
|
f98c683915 | ||
|
|
ffe9c9d947 | ||
|
|
4d42ffc7f8 | ||
|
|
d7dc7d911e | ||
|
|
04404ff61b | ||
|
|
f864e15c68 | ||
|
|
29a444fffc | ||
|
|
327035a43a | ||
|
|
ad7fed061e | ||
|
|
cea2986574 | ||
|
|
00f0fd7109 | ||
|
|
1c6481d011 | ||
|
|
f0bc926640 | ||
|
|
11d9ff5690 | ||
|
|
6a0a909e73 | ||
|
|
bd8f0c88e5 | ||
|
|
e5809e49cb | ||
|
|
880009b648 | ||
|
|
d083c7f1d0 | ||
|
|
684ce141de | ||
|
|
5c8c7a043a | ||
|
|
0fe7f65ef2 | ||
|
|
950c1de46d | ||
|
|
fc690f14a8 | ||
|
|
a6e03e4d11 | ||
|
|
60428be5fb | ||
|
|
4d402d4875 | ||
|
|
fbddd264be | ||
|
|
d3c46d3f7c | ||
|
|
dda3984a8f | ||
|
|
f889ca8885 | ||
|
|
341bacb9a2 | ||
|
|
badc1177d9 | ||
|
|
1680cc72c0 | ||
|
|
2f89de86f8 | ||
|
|
ab4e9c718b | ||
|
|
288c03ddca | ||
|
|
5c60285f25 | ||
|
|
6df0a45368 | ||
|
|
8206987580 | ||
|
|
6189ca9798 | ||
|
|
b8c1601a93 | ||
|
|
34d610a38d | ||
|
|
8e4c8464e7 | ||
|
|
43ab7a8e71 | ||
|
|
d2f23a4b8b | ||
|
|
0dc36a4f7c | ||
|
|
678ae12115 | ||
|
|
c4d937322f | ||
|
|
a2adb835b6 | ||
|
|
01b5c4e9da | ||
|
|
eb4c5dc035 | ||
|
|
e976386931 | ||
|
|
bae9986422 | ||
|
|
e7970d82a8 | ||
|
|
9bdd489c96 | ||
|
|
0f49fef5ed | ||
|
|
8d74b8c3b5 | ||
|
|
39a8798a87 | ||
|
|
980f407552 | ||
|
|
9ca8f4602d | ||
|
|
ba0f583ee5 | ||
|
|
f432f4d75e | ||
|
|
17793abacd | ||
|
|
64cd4ec1d5 | ||
|
|
fb822984e3 | ||
|
|
6d2a737c29 | ||
|
|
6e7713281e | ||
|
|
6a7bb9e33e | ||
|
|
0e9428c8b0 | ||
|
|
c838962432 | ||
|
|
548d10ef08 | ||
|
|
2db8487f02 | ||
|
|
b42f71a20f | ||
|
|
e9824aa749 | ||
|
|
92cc9fc5e1 | ||
|
|
e53b4c9884 | ||
|
|
e2973f6ecc | ||
|
|
2bf9fc816d | ||
|
|
ff55856c63 | ||
|
|
ceef219eec | ||
|
|
b21699a277 | ||
|
|
2ab85d2f63 | ||
|
|
320545884c | ||
|
|
16bbc2d67e | ||
|
|
6d860e8ace | ||
|
|
80c8a6df61 | ||
|
|
90bc96d9d8 | ||
|
|
2be59aefec | ||
|
|
91b779269f | ||
|
|
e6ea4ad260 | ||
|
|
f5cab0f6e1 | ||
|
|
e39afc9f68 | ||
|
|
3801a29d9b | ||
|
|
39cf4af638 | ||
|
|
9d65dd0786 | ||
|
|
3dced70850 | ||
|
|
4356043582 | ||
|
|
c202be0338 | ||
|
|
9359155418 | ||
|
|
ba22bbe19e | ||
|
|
3e5989ae18 | ||
|
|
fbebd990a4 | ||
|
|
257707d839 | ||
|
|
c1cd872df6 | ||
|
|
646e0b4f61 | ||
|
|
0f67692265 | ||
|
|
6a7afd8694 | ||
|
|
46194b3385 | ||
|
|
904a9dea08 | ||
|
|
30b58e90a3 | ||
|
|
5bdd8657a5 | ||
|
|
893c99da1c | ||
|
|
43980f8586 | ||
|
|
0c8e033c96 | ||
|
|
fa15242719 | ||
|
|
a2e080bf6c | ||
|
|
e91cdb5eba | ||
|
|
ef54f9be5a | ||
|
|
f051acb83c | ||
|
|
bbb046081b | ||
|
|
77b28a986f | ||
|
|
97bc34eb37 | ||
|
|
719380f523 | ||
|
|
640ae9521b | ||
|
|
1dffc7577b |
27
Makefile
27
Makefile
@@ -15,7 +15,8 @@ verify-kustomize: \
|
|||||||
lint-kustomize \
|
lint-kustomize \
|
||||||
test-unit-kustomize-all \
|
test-unit-kustomize-all \
|
||||||
test-examples-kustomize-against-HEAD \
|
test-examples-kustomize-against-HEAD \
|
||||||
test-examples-kustomize-against-3.8.0
|
test-examples-kustomize-against-3.8.0 \
|
||||||
|
test-examples-kustomize-against-3.8.2
|
||||||
|
|
||||||
# The following target referenced by a file in
|
# The following target referenced by a file in
|
||||||
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
||||||
@@ -26,7 +27,8 @@ prow-presubmit-check: \
|
|||||||
test-unit-cmd-all \
|
test-unit-cmd-all \
|
||||||
test-go-mod \
|
test-go-mod \
|
||||||
test-examples-kustomize-against-HEAD \
|
test-examples-kustomize-against-HEAD \
|
||||||
test-examples-kustomize-against-3.8.0
|
test-examples-kustomize-against-3.8.0 \
|
||||||
|
test-examples-kustomize-against-3.8.2
|
||||||
|
|
||||||
.PHONY: verify-kustomize-e2e
|
.PHONY: verify-kustomize-e2e
|
||||||
verify-kustomize-e2e: test-examples-e2e-kustomize
|
verify-kustomize-e2e: test-examples-e2e-kustomize
|
||||||
@@ -45,17 +47,18 @@ $(MYGOBIN)/golangci-lint-kustomize:
|
|||||||
GO111MODULE=on go build -tags=tools -o $(MYGOBIN)/golangci-lint-kustomize github.com/golangci/golangci-lint/cmd/golangci-lint; \
|
GO111MODULE=on go build -tags=tools -o $(MYGOBIN)/golangci-lint-kustomize github.com/golangci/golangci-lint/cmd/golangci-lint; \
|
||||||
)
|
)
|
||||||
|
|
||||||
# Version pinned by api/go.mod
|
$(MYGOBIN)/gorepomod:
|
||||||
|
cd api; \
|
||||||
|
go install github.com/monopole/gorepomod
|
||||||
|
|
||||||
$(MYGOBIN)/mdrip:
|
$(MYGOBIN)/mdrip:
|
||||||
cd api; \
|
cd api; \
|
||||||
go install github.com/monopole/mdrip
|
go install github.com/monopole/mdrip
|
||||||
|
|
||||||
# Version pinned by api/go.mod
|
|
||||||
$(MYGOBIN)/stringer:
|
$(MYGOBIN)/stringer:
|
||||||
cd api; \
|
cd api; \
|
||||||
go install golang.org/x/tools/cmd/stringer
|
go install golang.org/x/tools/cmd/stringer
|
||||||
|
|
||||||
# Version pinned by api/go.mod
|
|
||||||
$(MYGOBIN)/goimports:
|
$(MYGOBIN)/goimports:
|
||||||
cd api; \
|
cd api; \
|
||||||
go install golang.org/x/tools/cmd/goimports
|
go install golang.org/x/tools/cmd/goimports
|
||||||
@@ -81,6 +84,7 @@ $(MYGOBIN)/kustomize:
|
|||||||
install-tools: \
|
install-tools: \
|
||||||
$(MYGOBIN)/goimports \
|
$(MYGOBIN)/goimports \
|
||||||
$(MYGOBIN)/golangci-lint-kustomize \
|
$(MYGOBIN)/golangci-lint-kustomize \
|
||||||
|
$(MYGOBIN)/gorepomod \
|
||||||
$(MYGOBIN)/mdrip \
|
$(MYGOBIN)/mdrip \
|
||||||
$(MYGOBIN)/pluginator \
|
$(MYGOBIN)/pluginator \
|
||||||
$(MYGOBIN)/stringer
|
$(MYGOBIN)/stringer
|
||||||
@@ -245,6 +249,19 @@ test-examples-kustomize-against-3.8.0: $(MYGOBIN)/mdrip
|
|||||||
cd kustomize; go install .; \
|
cd kustomize; go install .; \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
.PHONY:
|
||||||
|
test-examples-kustomize-against-3.8.2: $(MYGOBIN)/mdrip
|
||||||
|
( \
|
||||||
|
set -e; \
|
||||||
|
tag=v3.8.2; \
|
||||||
|
/bin/rm -f $(MYGOBIN)/kustomize; \
|
||||||
|
echo "Installing kustomize $$tag."; \
|
||||||
|
GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3@$${tag}; \
|
||||||
|
./hack/testExamplesAgainstKustomize.sh $$tag; \
|
||||||
|
echo "Reinstalling kustomize from HEAD."; \
|
||||||
|
cd kustomize; go install .; \
|
||||||
|
)
|
||||||
|
|
||||||
# linux only.
|
# linux only.
|
||||||
# This is for testing an example plugin that
|
# This is for testing an example plugin that
|
||||||
# uses kubeval for validation.
|
# uses kubeval for validation.
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ aliases:
|
|||||||
- pwittrock
|
- pwittrock
|
||||||
- mortent
|
- mortent
|
||||||
- phanimarupaka
|
- phanimarupaka
|
||||||
|
- Shell32-Natsu
|
||||||
|
|||||||
@@ -31,14 +31,15 @@ func (p *ImageTagTransformerPlugin) Config(
|
|||||||
|
|
||||||
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
|
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
for _, r := range m.Resources() {
|
for _, r := range m.Resources() {
|
||||||
// If you're here because someone expected any field containing
|
// traverse all fields at first
|
||||||
// the string "containers" or "initContainers" to get an image
|
err := filtersutil.ApplyToJSON(imagetag.LegacyFilter{
|
||||||
// update (not just spec/template/spec/containers[], etc.) then
|
ImageTag: p.ImageTag,
|
||||||
// a code change is needed. See api/filters/imagetag/legacy
|
}, r)
|
||||||
// for the start of an implementation that won't use an
|
if err != nil {
|
||||||
// allowlist like FsSlice, and instead walks the object looking
|
return err
|
||||||
// for fields named containers or initContainers.
|
}
|
||||||
err := filtersutil.ApplyToJSON(imagetag.Filter{
|
// then use user specified field specs
|
||||||
|
err = filtersutil.ApplyToJSON(imagetag.Filter{
|
||||||
ImageTag: p.ImageTag,
|
ImageTag: p.ImageTag,
|
||||||
FsSlice: p.FieldSpecs,
|
FsSlice: p.FieldSpecs,
|
||||||
}, r)
|
}, r)
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ func (fltr Filter) filter(obj *yaml.RNode) error {
|
|||||||
// found the field -- set its value
|
// found the field -- set its value
|
||||||
return fltr.SetValue(obj)
|
return fltr.SetValue(obj)
|
||||||
}
|
}
|
||||||
|
if obj.IsTaggedNull() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
switch obj.YNode().Kind {
|
switch obj.YNode().Kind {
|
||||||
case yaml.SequenceNode:
|
case yaml.SequenceNode:
|
||||||
return fltr.seq(obj)
|
return fltr.seq(obj)
|
||||||
@@ -67,7 +70,7 @@ func (fltr Filter) field(obj *yaml.RNode) error {
|
|||||||
// lookup the field matching the next path element
|
// lookup the field matching the next path element
|
||||||
var lookupField yaml.Filter
|
var lookupField yaml.Filter
|
||||||
var kind yaml.Kind
|
var kind yaml.Kind
|
||||||
tag := "" // TODO: change to yaml.NodeTagEmpty
|
tag := yaml.NodeTagEmpty
|
||||||
switch {
|
switch {
|
||||||
case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq:
|
case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq:
|
||||||
// dont' create the field if we don't find it
|
// dont' create the field if we don't find it
|
||||||
@@ -95,9 +98,10 @@ func (fltr Filter) field(obj *yaml.RNode) error {
|
|||||||
return errors.WrapPrefixf(err, "fieldName: %s", fieldName)
|
return errors.WrapPrefixf(err, "fieldName: %s", fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the value exists, but is null, then change it to the creation type
|
// if the value exists, but is null and kind is set,
|
||||||
|
// then change it to the creation type
|
||||||
// TODO: update yaml.LookupCreate to support this
|
// TODO: update yaml.LookupCreate to support this
|
||||||
if field.YNode().Tag == yaml.NodeTagNull {
|
if field.YNode().Tag == yaml.NodeTagNull && yaml.IsCreate(kind) {
|
||||||
field.YNode().Kind = kind
|
field.YNode().Kind = kind
|
||||||
field.YNode().Tag = tag
|
field.YNode().Tag = tag
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -433,7 +433,6 @@ spec:
|
|||||||
SetValue: filtersutil.SetScalar("bar"),
|
SetValue: filtersutil.SetScalar("bar"),
|
||||||
CreateKind: yaml.ScalarNode,
|
CreateKind: yaml.ScalarNode,
|
||||||
},
|
},
|
||||||
error: "obj '' at path 'spec/containers/image': expected sequence or mapping node",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "filedname with slash '/'",
|
name: "filedname with slash '/'",
|
||||||
|
|||||||
@@ -86,6 +86,18 @@ func (f Filter) setScalar(node *yaml.RNode) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterReferralCandidates(
|
||||||
|
referrer *resource.Resource,
|
||||||
|
matches []*resource.Resource) []*resource.Resource {
|
||||||
|
var ret []*resource.Resource
|
||||||
|
for _, m := range matches {
|
||||||
|
if referrer.PrefixesSuffixesEquals(m) {
|
||||||
|
ret = append(ret, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// selectReferral picks the referral among a subset of candidates.
|
// selectReferral picks the referral among a subset of candidates.
|
||||||
// It returns the current name and namespace of the selected candidate.
|
// It returns the current name and namespace of the selected candidate.
|
||||||
// Note that the content of the referricalCandidateSubset slice is most of the time
|
// Note that the content of the referricalCandidateSubset slice is most of the time
|
||||||
@@ -103,12 +115,19 @@ func selectReferral(
|
|||||||
id := res.OrgId()
|
id := res.OrgId()
|
||||||
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
|
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
|
||||||
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
|
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
|
||||||
// If there's more than one match, there's no way
|
// If there's more than one match,
|
||||||
// to know which one to pick, so emit error.
|
// filter the matches by prefix and suffix
|
||||||
if len(matches) > 1 {
|
if len(matches) > 1 {
|
||||||
return "", "", fmt.Errorf(
|
filteredMatches := filterReferralCandidates(referrer, matches)
|
||||||
"multiple matches for %s:\n %v",
|
if len(filteredMatches) > 1 {
|
||||||
id, getIds(matches))
|
return "", "", fmt.Errorf(
|
||||||
|
"multiple matches for %s:\n %v",
|
||||||
|
id, getIds(filteredMatches))
|
||||||
|
}
|
||||||
|
// Check is the match the resource we are working on
|
||||||
|
if len(filteredMatches) == 0 || res != filteredMatches[0] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// In the resource, note that it is referenced
|
// In the resource, note that it is referenced
|
||||||
// by the referrer.
|
// by the referrer.
|
||||||
|
|||||||
@@ -168,6 +168,44 @@ metadata:
|
|||||||
map:
|
map:
|
||||||
name: newName
|
name: newName
|
||||||
namespace: oldNs
|
namespace: oldNs
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "map"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"null value": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
map:
|
||||||
|
name: null
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: NotSecret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", ""},
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
map:
|
||||||
|
name: null
|
||||||
`,
|
`,
|
||||||
filter: Filter{
|
filter: Filter{
|
||||||
FieldSpec: types.FieldSpec{Path: "map"},
|
FieldSpec: types.FieldSpec{Path: "map"},
|
||||||
@@ -217,27 +255,6 @@ func TestNamerefFilterUnhappy(t *testing.T) {
|
|||||||
filter Filter
|
filter Filter
|
||||||
originalNames []string
|
originalNames []string
|
||||||
}{
|
}{
|
||||||
"invalid node type": {
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: dep
|
|
||||||
ref:
|
|
||||||
name: null
|
|
||||||
`,
|
|
||||||
candidates: "",
|
|
||||||
originalNames: []string{},
|
|
||||||
expected: "obj '' at path 'ref/name': node is expected to be either a string or a slice of string or a map of string",
|
|
||||||
filter: Filter{
|
|
||||||
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
|
||||||
Target: resid.Gvk{
|
|
||||||
Group: "apps",
|
|
||||||
Version: "v1",
|
|
||||||
Kind: "Secret",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"multiple match": {
|
"multiple match": {
|
||||||
input: `
|
input: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
@@ -331,3 +348,434 @@ metadata:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCandidatesWithDifferentPrefixSuffix(t *testing.T) {
|
||||||
|
testCases := map[string]struct {
|
||||||
|
input string
|
||||||
|
candidates string
|
||||||
|
expected string
|
||||||
|
filter Filter
|
||||||
|
originalNames []string
|
||||||
|
prefix []string
|
||||||
|
suffix []string
|
||||||
|
inputPrefix string
|
||||||
|
inputSuffix string
|
||||||
|
err bool
|
||||||
|
}{
|
||||||
|
"prefix match": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"prefix1", "prefix2"},
|
||||||
|
suffix: []string{"", "suffix2"},
|
||||||
|
inputPrefix: "prefix1",
|
||||||
|
inputSuffix: "",
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: newName
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
"suffix match": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"", "prefix2"},
|
||||||
|
suffix: []string{"suffix1", "suffix2"},
|
||||||
|
inputPrefix: "",
|
||||||
|
inputSuffix: "suffix1",
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: newName
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
"prefix suffix both match": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"prefix1", "prefix2"},
|
||||||
|
suffix: []string{"suffix1", "suffix2"},
|
||||||
|
inputPrefix: "prefix1",
|
||||||
|
inputSuffix: "suffix1",
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: newName
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
"multiple match: both": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"prefix", "prefix"},
|
||||||
|
suffix: []string{"suffix", "suffix"},
|
||||||
|
inputPrefix: "prefix",
|
||||||
|
inputSuffix: "suffix",
|
||||||
|
expected: "",
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
"multiple match: only prefix": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"prefix", "prefix"},
|
||||||
|
suffix: []string{"", ""},
|
||||||
|
inputPrefix: "prefix",
|
||||||
|
inputSuffix: "",
|
||||||
|
expected: "",
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
"multiple match: only suffix": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"", ""},
|
||||||
|
suffix: []string{"suffix", "suffix"},
|
||||||
|
inputPrefix: "",
|
||||||
|
inputSuffix: "suffix",
|
||||||
|
expected: "",
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
"no match: neither match": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"prefix1", "prefix2"},
|
||||||
|
suffix: []string{"suffix1", "suffix2"},
|
||||||
|
inputPrefix: "prefix",
|
||||||
|
inputSuffix: "suffix",
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
"no match: prefix doesn't match": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"prefix1", "prefix2"},
|
||||||
|
suffix: []string{"suffix", "suffix"},
|
||||||
|
inputPrefix: "prefix",
|
||||||
|
inputSuffix: "suffix",
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
"no match: suffix doesn't match": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
candidates: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: newName2
|
||||||
|
`,
|
||||||
|
originalNames: []string{"oldName", "oldName"},
|
||||||
|
prefix: []string{"prefix", "prefix"},
|
||||||
|
suffix: []string{"suffix1", "suffix2"},
|
||||||
|
inputPrefix: "prefix",
|
||||||
|
inputSuffix: "suffix",
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
ref:
|
||||||
|
name: oldName
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
FieldSpec: types.FieldSpec{Path: "ref/name"},
|
||||||
|
Target: resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tn, tc := range testCases {
|
||||||
|
t.Run(tn, func(t *testing.T) {
|
||||||
|
factory := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||||
|
referrer, err := factory.FromBytes([]byte(tc.input))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if tc.inputPrefix != "" {
|
||||||
|
referrer.AddNamePrefix(tc.inputPrefix)
|
||||||
|
}
|
||||||
|
if tc.inputSuffix != "" {
|
||||||
|
referrer.AddNameSuffix(tc.inputSuffix)
|
||||||
|
}
|
||||||
|
tc.filter.Referrer = referrer
|
||||||
|
|
||||||
|
resMapFactory := resmap.NewFactory(factory, nil)
|
||||||
|
candidatesRes, err := factory.SliceFromBytesWithNames(
|
||||||
|
tc.originalNames, []byte(tc.candidates))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i := range candidatesRes {
|
||||||
|
if tc.prefix[i] != "" {
|
||||||
|
candidatesRes[i].AddNamePrefix(tc.prefix[i])
|
||||||
|
}
|
||||||
|
if tc.suffix[i] != "" {
|
||||||
|
candidatesRes[i].AddNameSuffix(tc.suffix[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates := resMapFactory.FromResourceSlice(candidatesRes)
|
||||||
|
tc.filter.ReferralCandidates = candidates
|
||||||
|
|
||||||
|
if !tc.err {
|
||||||
|
if !assert.Equal(t,
|
||||||
|
strings.TrimSpace(tc.expected),
|
||||||
|
strings.TrimSpace(
|
||||||
|
filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err := filtertest_test.RunFilterE(t, tc.input, tc.filter)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("an error is expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func (ns Filter) hacks(obj *yaml.RNode) error {
|
|||||||
// metaNamespaceHack is a hack for implementing the namespace transform
|
// metaNamespaceHack is a hack for implementing the namespace transform
|
||||||
// for the metadata.namespace field on namespace scoped resources.
|
// for the metadata.namespace field on namespace scoped resources.
|
||||||
// namespace scoped resources are determined by NOT being present
|
// namespace scoped resources are determined by NOT being present
|
||||||
// in a blacklist of cluster-scoped resource types (by apiVersion and kind).
|
// in a hard-coded list of cluster-scoped resource types (by apiVersion and kind).
|
||||||
//
|
//
|
||||||
// This hack should be updated to allow individual resources to specify
|
// This hack should be updated to allow individual resources to specify
|
||||||
// if they are cluster scoped through either an annotation on the resources,
|
// if they are cluster scoped through either an annotation on the resources,
|
||||||
|
|||||||
@@ -188,6 +188,26 @@ data:
|
|||||||
FieldSpec: types.FieldSpec{Path: "data/slice2"},
|
FieldSpec: types.FieldSpec{Path: "data/slice2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"null value": {
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
data:
|
||||||
|
FOO: null`,
|
||||||
|
expected: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: dep
|
||||||
|
data:
|
||||||
|
FOO: null`,
|
||||||
|
filter: Filter{
|
||||||
|
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{}),
|
||||||
|
FieldSpec: types.FieldSpec{Path: "data/FOO"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
@@ -260,20 +280,6 @@ data:
|
|||||||
FieldSpec: types.FieldSpec{Path: "data"},
|
FieldSpec: types.FieldSpec{Path: "data"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"null input": {
|
|
||||||
input: `
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: dep
|
|
||||||
data:
|
|
||||||
FOO: null`,
|
|
||||||
expectedError: "obj '' at path 'data/FOO': invalid type encountered 0",
|
|
||||||
filter: Filter{
|
|
||||||
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{}),
|
|
||||||
FieldSpec: types.FieldSpec{Path: "data/FOO"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
|
|||||||
@@ -10,14 +10,13 @@ require (
|
|||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/yujunz/go-getter v1.4.1-lite
|
github.com/yujunz/go-getter v1.4.1-lite
|
||||||
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
|
||||||
gopkg.in/yaml.v2 v2.3.0
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
|
||||||
k8s.io/api v0.17.0
|
k8s.io/api v0.17.0
|
||||||
k8s.io/apimachinery v0.17.0
|
k8s.io/apimachinery v0.17.0
|
||||||
k8s.io/client-go v0.17.0
|
k8s.io/client-go v0.17.0
|
||||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
|
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
|
||||||
sigs.k8s.io/kustomize/kyaml v0.5.0
|
sigs.k8s.io/kustomize/kyaml v0.8.0
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/kyaml v0.5.0 => ../kyaml
|
|
||||||
|
|||||||
@@ -307,6 +307,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
@@ -526,6 +528,8 @@ golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs=
|
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs=
|
||||||
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
@@ -584,6 +588,8 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
|
|||||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
|
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
|
||||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.8.0 h1:/MqPML99XAm2pbrD/eTpePh5rnU5bpnuTPqb29LpSz4=
|
||||||
|
sigs.k8s.io/kustomize/kyaml v0.8.0/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ type Loader interface {
|
|||||||
Cleanup() error
|
Cleanup() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kunstructured allows manipulation of k8s objects
|
// Kunstructured represents a Kubernetes Resource Model object.
|
||||||
// that do not have Golang structs.
|
|
||||||
type Kunstructured interface {
|
type Kunstructured interface {
|
||||||
// Several uses.
|
// Several uses.
|
||||||
Copy() Kunstructured
|
Copy() Kunstructured
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func TestRefVarTransformer(t *testing.T) {
|
|||||||
' at path 'data/slice': invalid value type expect a string`,
|
' at path 'data/slice': invalid value type expect a string`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "var replacement panic in nil",
|
description: "var replacement in nil",
|
||||||
given: given{
|
given: given{
|
||||||
varMap: map[string]interface{}{},
|
varMap: map[string]interface{}{},
|
||||||
fs: []types.FieldSpec{
|
fs: []types.FieldSpec{
|
||||||
@@ -150,7 +150,19 @@ func TestRefVarTransformer(t *testing.T) {
|
|||||||
"nil": nil, // noticeably *not* a []string
|
"nil": nil, // noticeably *not* a []string
|
||||||
}}).ResMap(),
|
}}).ResMap(),
|
||||||
},
|
},
|
||||||
errMessage: `obj '' at path 'data/nil': invalid type encountered 0`,
|
expected: expected{
|
||||||
|
res: resmaptest_test.NewRmBuilder(
|
||||||
|
t, resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"nil": nil, // noticeably *not* a []string
|
||||||
|
}}).ResMap(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ overview of each component with the following sections going into more details.
|
|||||||
|
|
||||||
The overall structure is outlined in the following figure:
|
The overall structure is outlined in the following figure:
|
||||||

|
https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/crawl/pictures/token_config.png)
|
||||||
|
|
||||||
#### Crawler
|
#### Crawler
|
||||||
The leftmost component consists of a crawler with an http cache of GitHub
|
The leftmost component consists of a crawler with an http cache of GitHub
|
||||||
|
|||||||
@@ -271,6 +271,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
@@ -338,6 +340,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
|
|||||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
@@ -469,6 +472,7 @@ golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDq
|
|||||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
@@ -523,8 +527,8 @@ k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl
|
|||||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
sigs.k8s.io/kustomize/kyaml v0.8.0 h1:/MqPML99XAm2pbrD/eTpePh5rnU5bpnuTPqb29LpSz4=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
sigs.k8s.io/kustomize/kyaml v0.8.0/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
|||||||
25
api/internal/merge/merginator.go
Normal file
25
api/internal/merge/merginator.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package merge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Merginator implements resmap.Merginator using kyaml libs.
|
||||||
|
type Merginator struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ resmap.Merginator = (*Merginator)(nil)
|
||||||
|
|
||||||
|
func NewMerginator(_ *resource.Factory) *Merginator {
|
||||||
|
return &Merginator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge implements resmap.Merginator
|
||||||
|
func (m Merginator) Merge(
|
||||||
|
resources []*resource.Resource) (resmap.ResMap, error) {
|
||||||
|
panic("TODO(#Merginator): implement Merge")
|
||||||
|
}
|
||||||
4
api/internal/merge/merginator_test.go
Normal file
4
api/internal/merge/merginator_test.go
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package merge_test
|
||||||
64
api/internal/validate/fieldvalidator.go
Normal file
64
api/internal/validate/fieldvalidator.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package validate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FieldValidator implements ifc.Validator to check
|
||||||
|
// the values of various KRM string fields,
|
||||||
|
// e.g. labels, annotations, names, namespaces.
|
||||||
|
type FieldValidator struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ifc.Validator = (*FieldValidator)(nil)
|
||||||
|
|
||||||
|
func NewFieldValidator() *FieldValidator {
|
||||||
|
return &FieldValidator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#FieldValidator): implement MakeAnnotationValidator
|
||||||
|
func (f FieldValidator) MakeAnnotationValidator() func(map[string]string) error {
|
||||||
|
return func(x map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#FieldValidator): implement MakeAnnotationNameValidator
|
||||||
|
func (f FieldValidator) MakeAnnotationNameValidator() func([]string) error {
|
||||||
|
return func(x []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#FieldValidator): implement MakeLabelValidator
|
||||||
|
func (f FieldValidator) MakeLabelValidator() func(map[string]string) error {
|
||||||
|
return func(x map[string]string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#FieldValidator): implement MakeLabelNameValidator
|
||||||
|
func (f FieldValidator) MakeLabelNameValidator() func([]string) error {
|
||||||
|
return func(x []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#FieldValidator): implement ValidateNamespace
|
||||||
|
func (f FieldValidator) ValidateNamespace(s string) []string {
|
||||||
|
var errs []string
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#FieldValidator): implement ErrIfInvalidKey
|
||||||
|
func (f FieldValidator) ErrIfInvalidKey(s string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(#FieldValidator): implement IsEnvVarName
|
||||||
|
func (f FieldValidator) IsEnvVarName(k string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
4
api/internal/validate/fieldvalidator_test.go
Normal file
4
api/internal/validate/fieldvalidator_test.go
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package validate_test
|
||||||
41
api/internal/wrappy/factory.go
Normal file
41
api/internal/wrappy/factory.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package wrappy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WNodeFactory makes instances of WNode.
|
||||||
|
// These instances in turn adapt
|
||||||
|
// sigs.k8s.io/kustomize/kyaml/yaml.RNode
|
||||||
|
// to implement ifc.Unstructured.
|
||||||
|
// This factory is meant to implement ifc.KunstructuredFactory.
|
||||||
|
type WNodeFactory struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ifc.KunstructuredFactory = (*WNodeFactory)(nil)
|
||||||
|
|
||||||
|
func (k *WNodeFactory) SliceFromBytes(bs []byte) ([]ifc.Kunstructured, error) {
|
||||||
|
panic("TODO(#WNodeFactory): implement SliceFromBytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *WNodeFactory) FromMap(m map[string]interface{}) ifc.Kunstructured {
|
||||||
|
panic("TODO(#WNodeFactory): implement FromMap")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *WNodeFactory) Hasher() ifc.KunstructuredHasher {
|
||||||
|
panic("TODO(#WNodeFactory): implement Hasher")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *WNodeFactory) MakeConfigMap(
|
||||||
|
kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
|
||||||
|
panic("TODO(#WNodeFactory): implement MakeConfigMap")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *WNodeFactory) MakeSecret(
|
||||||
|
kvLdr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
|
||||||
|
panic("TODO(#WNodeFactory): implement MakeSecret")
|
||||||
|
}
|
||||||
4
api/internal/wrappy/factory_test.go
Normal file
4
api/internal/wrappy/factory_test.go
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package wrappy_test
|
||||||
143
api/internal/wrappy/wnode.go
Normal file
143
api/internal/wrappy/wnode.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package wrappy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WNode implements ifc.Kunstructured using yaml.RNode.
|
||||||
|
//
|
||||||
|
// It exists only to help manage a switch from
|
||||||
|
// kunstruct.UnstructAdapter to yaml.RNode as the core
|
||||||
|
// representation of KRM objects in kustomize.
|
||||||
|
//
|
||||||
|
// It's got a silly name because we don't want it around for long,
|
||||||
|
// and want its use to be obvious.
|
||||||
|
type WNode struct {
|
||||||
|
node *yaml.RNode
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ifc.Kunstructured = (*WNode)(nil)
|
||||||
|
|
||||||
|
func NewWNode() *WNode {
|
||||||
|
return FromRNode(yaml.NewRNode(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromRNode(node *yaml.RNode) *WNode {
|
||||||
|
return &WNode{node: node}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (wn *WNode) demandMetaData(label string) yaml.ResourceMeta {
|
||||||
|
meta, err := wn.node.GetMeta()
|
||||||
|
if err != nil {
|
||||||
|
// Log and die since interface doesn't allow error.
|
||||||
|
log.Fatalf("for %s', expected valid resource: %v", label, err)
|
||||||
|
}
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) Copy() ifc.Kunstructured {
|
||||||
|
return &WNode{node: wn.node.Copy()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAnnotations implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetAnnotations() map[string]string {
|
||||||
|
return wn.demandMetaData("GetAnnotations").Annotations
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFieldValue implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetFieldValue(path string) (interface{}, error) {
|
||||||
|
// The argument is a json path, e.g. "metadata.name"
|
||||||
|
// fields := strings.Split(path, ".")
|
||||||
|
// return wn.node.Pipe(yaml.Lookup(fields...))
|
||||||
|
panic("TODO(#WNode): GetFieldValue; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGvk implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetGvk() resid.Gvk {
|
||||||
|
meta := wn.demandMetaData("GetGvk")
|
||||||
|
g, v := resid.ParseGroupVersion(meta.APIVersion)
|
||||||
|
return resid.Gvk{Group: g, Version: v, Kind: meta.Kind}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetKind implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetKind() string {
|
||||||
|
return wn.demandMetaData("GetKind").Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLabels implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetLabels() map[string]string {
|
||||||
|
return wn.demandMetaData("GetLabels").Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetName() string {
|
||||||
|
return wn.demandMetaData("GetName").Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlice implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetSlice(string) ([]interface{}, error) {
|
||||||
|
panic("TODO(#WNode) GetSlice; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlice implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) GetString(string) (string, error) {
|
||||||
|
panic("TODO(#WNode) GetString; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) Map() map[string]interface{} {
|
||||||
|
panic("TODO(#WNode) Map; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) MarshalJSON() ([]byte, error) {
|
||||||
|
return wn.node.MarshalJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesAnnotationSelector implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) MatchesAnnotationSelector(string) (bool, error) {
|
||||||
|
panic("TODO(#WNode) MatchesAnnotationSelector; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesLabelSelector implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) MatchesLabelSelector(string) (bool, error) {
|
||||||
|
panic("TODO(#WNode) MatchesLabelSelector; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAnnotations implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) SetAnnotations(map[string]string) {
|
||||||
|
panic("TODO(#WNode) SetAnnotations; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetGvk implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) SetGvk(resid.Gvk) {
|
||||||
|
panic("TODO(#WNode) SetGvk; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabels implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) SetLabels(map[string]string) {
|
||||||
|
panic("TODO(#WNode) SetLabels; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) SetName(string) {
|
||||||
|
panic("TODO(#WNode) SetName; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNamespace implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) SetNamespace(string) {
|
||||||
|
panic("TODO(#WNode) SetNamespace; implement or drop from API")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements ifc.Kunstructured.
|
||||||
|
func (wn *WNode) UnmarshalJSON(data []byte) error {
|
||||||
|
return wn.node.UnmarshalJSON(data)
|
||||||
|
}
|
||||||
339
api/internal/wrappy/wnode_test.go
Normal file
339
api/internal/wrappy/wnode_test.go
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package wrappy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/wrappy"
|
||||||
|
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
deploymentLittleJson = `{"apiVersion":"apps/v1","kind":"Deployment",` +
|
||||||
|
`"metadata":{"name":"homer","namespace":"simpsons"}}`
|
||||||
|
|
||||||
|
deploymentBiggerJson = `
|
||||||
|
{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": {
|
||||||
|
"name": "homer",
|
||||||
|
"namespace": "simpsons",
|
||||||
|
"labels": {
|
||||||
|
"fruit": "apple",
|
||||||
|
"veggie": "carrot"
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
"area": "51",
|
||||||
|
"greeting": "Take me to your leader."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
bigMapYaml = `Kind: Service
|
||||||
|
complextree:
|
||||||
|
- field1:
|
||||||
|
- boolfield: true
|
||||||
|
floatsubfield: 1.01
|
||||||
|
intsubfield: 1010
|
||||||
|
stringsubfield: idx1010
|
||||||
|
- boolfield: false
|
||||||
|
floatsubfield: 1.011
|
||||||
|
intsubfield: 1011
|
||||||
|
stringsubfield: idx1011
|
||||||
|
field2:
|
||||||
|
- boolfield: true
|
||||||
|
floatsubfield: 1.02
|
||||||
|
intsubfield: 1020
|
||||||
|
stringsubfield: idx1020
|
||||||
|
- boolfield: false
|
||||||
|
floatsubfield: 1.021
|
||||||
|
intsubfield: 1021
|
||||||
|
stringsubfield: idx1021
|
||||||
|
- field1:
|
||||||
|
- boolfield: true
|
||||||
|
floatsubfield: 1.11
|
||||||
|
intsubfield: 1110
|
||||||
|
stringsubfield: idx1110
|
||||||
|
- boolfield: false
|
||||||
|
floatsubfield: 1.111
|
||||||
|
intsubfield: 1111
|
||||||
|
stringsubfield: idx1111
|
||||||
|
field2:
|
||||||
|
- boolfield: true
|
||||||
|
floatsubfield: 1.112
|
||||||
|
intsubfield: 1120
|
||||||
|
stringsubfield: idx1120
|
||||||
|
- boolfield: false
|
||||||
|
floatsubfield: 1.1121
|
||||||
|
intsubfield: 1121
|
||||||
|
stringsubfield: idx1121
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: application-name
|
||||||
|
name: service-name
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
port: 80
|
||||||
|
that:
|
||||||
|
- idx0
|
||||||
|
- idx1
|
||||||
|
- idx2
|
||||||
|
- idx3
|
||||||
|
these:
|
||||||
|
- field1:
|
||||||
|
- idx010
|
||||||
|
- idx011
|
||||||
|
field2:
|
||||||
|
- idx020
|
||||||
|
- idx021
|
||||||
|
- field1:
|
||||||
|
- idx110
|
||||||
|
- idx111
|
||||||
|
field2:
|
||||||
|
- idx120
|
||||||
|
- idx121
|
||||||
|
- field1:
|
||||||
|
- idx210
|
||||||
|
- idx211
|
||||||
|
field2:
|
||||||
|
- idx220
|
||||||
|
- idx221
|
||||||
|
this:
|
||||||
|
is:
|
||||||
|
aBool: true
|
||||||
|
aFloat: 1.001
|
||||||
|
aNilValue: null
|
||||||
|
aNumber: 1000
|
||||||
|
anEmptyMap: {}
|
||||||
|
anEmptySlice: []
|
||||||
|
those:
|
||||||
|
- field1: idx0foo
|
||||||
|
field2: idx0bar
|
||||||
|
- field1: idx1foo
|
||||||
|
field2: idx1bar
|
||||||
|
- field1: idx2foo
|
||||||
|
field2: idx2bar
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeBigMap() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"Kind": "Service",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "application-name",
|
||||||
|
},
|
||||||
|
"name": "service-name",
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"ports": map[string]interface{}{
|
||||||
|
"port": int64(80),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"this": map[string]interface{}{
|
||||||
|
"is": map[string]interface{}{
|
||||||
|
"aNumber": int64(1000),
|
||||||
|
"aFloat": float64(1.001),
|
||||||
|
"aNilValue": nil,
|
||||||
|
"aBool": true,
|
||||||
|
"anEmptyMap": map[string]interface{}{},
|
||||||
|
"anEmptySlice": []interface{}{},
|
||||||
|
/*
|
||||||
|
TODO: test for unrecognizable (e.g. a function)
|
||||||
|
"unrecognizable": testing.InternalExample{
|
||||||
|
Name: "fooBar",
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"that": []interface{}{
|
||||||
|
"idx0",
|
||||||
|
"idx1",
|
||||||
|
"idx2",
|
||||||
|
"idx3",
|
||||||
|
},
|
||||||
|
"those": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": "idx0foo",
|
||||||
|
"field2": "idx0bar",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": "idx1foo",
|
||||||
|
"field2": "idx1bar",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": "idx2foo",
|
||||||
|
"field2": "idx2bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"these": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": []interface{}{"idx010", "idx011"},
|
||||||
|
"field2": []interface{}{"idx020", "idx021"},
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": []interface{}{"idx110", "idx111"},
|
||||||
|
"field2": []interface{}{"idx120", "idx121"},
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": []interface{}{"idx210", "idx211"},
|
||||||
|
"field2": []interface{}{"idx220", "idx221"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"complextree": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1010",
|
||||||
|
"intsubfield": int64(1010),
|
||||||
|
"floatsubfield": float64(1.010),
|
||||||
|
"boolfield": true,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1011",
|
||||||
|
"intsubfield": int64(1011),
|
||||||
|
"floatsubfield": float64(1.011),
|
||||||
|
"boolfield": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"field2": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1020",
|
||||||
|
"intsubfield": int64(1020),
|
||||||
|
"floatsubfield": float64(1.020),
|
||||||
|
"boolfield": true,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1021",
|
||||||
|
"intsubfield": int64(1021),
|
||||||
|
"floatsubfield": float64(1.021),
|
||||||
|
"boolfield": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"field1": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1110",
|
||||||
|
"intsubfield": int64(1110),
|
||||||
|
"floatsubfield": float64(1.110),
|
||||||
|
"boolfield": true,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1111",
|
||||||
|
"intsubfield": int64(1111),
|
||||||
|
"floatsubfield": float64(1.111),
|
||||||
|
"boolfield": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"field2": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1120",
|
||||||
|
"intsubfield": int64(1120),
|
||||||
|
"floatsubfield": float64(1.1120),
|
||||||
|
"boolfield": true,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"stringsubfield": "idx1121",
|
||||||
|
"intsubfield": int64(1121),
|
||||||
|
"floatsubfield": float64(1.1121),
|
||||||
|
"boolfield": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasicYamlOperationFromMap(t *testing.T) {
|
||||||
|
bytes, err := yaml.Marshal(makeBigMap())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected yaml.Marshal err: %v", err)
|
||||||
|
}
|
||||||
|
if string(bytes) != bigMapYaml {
|
||||||
|
t.Fatalf("unexpected string equality")
|
||||||
|
}
|
||||||
|
rNode, err := kyaml.Parse(string(bytes))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected yaml.Marshal err: %v", err)
|
||||||
|
}
|
||||||
|
rNodeString := rNode.MustString()
|
||||||
|
// The result from MustString has more indentation
|
||||||
|
// than bigMapYaml.
|
||||||
|
rNodeStrings := strings.Split(rNodeString, "\n")
|
||||||
|
bigMapStrings := strings.Split(bigMapYaml, "\n")
|
||||||
|
if len(rNodeStrings) != len(bigMapStrings) {
|
||||||
|
t.Fatalf("line count mismatch")
|
||||||
|
}
|
||||||
|
for i := range rNodeStrings {
|
||||||
|
s1 := strings.TrimSpace(rNodeStrings[i])
|
||||||
|
s2 := strings.TrimSpace(bigMapStrings[i])
|
||||||
|
if s1 != s2 {
|
||||||
|
t.Fatalf("expected '%s'=='%s'", s1, s2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundTripJSON(t *testing.T) {
|
||||||
|
wn := NewWNode()
|
||||||
|
err := wn.UnmarshalJSON([]byte(deploymentLittleJson))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected UnmarshalJSON err: %v", err)
|
||||||
|
}
|
||||||
|
data, err := wn.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected MarshalJSON err: %v", err)
|
||||||
|
}
|
||||||
|
actual := string(data)
|
||||||
|
if actual != deploymentLittleJson {
|
||||||
|
t.Fatalf("expected %s, got %s", deploymentLittleJson, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGettingFields(t *testing.T) {
|
||||||
|
wn := NewWNode()
|
||||||
|
err := wn.UnmarshalJSON([]byte(deploymentBiggerJson))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected unmarshaljson err: %v", err)
|
||||||
|
}
|
||||||
|
gvk := wn.GetGvk()
|
||||||
|
expected := "apps"
|
||||||
|
actual := gvk.Group
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
|
}
|
||||||
|
expected = "v1"
|
||||||
|
actual = gvk.Version
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
|
}
|
||||||
|
expected = "Deployment"
|
||||||
|
actual = gvk.Kind
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
|
}
|
||||||
|
actual = wn.GetKind()
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
|
}
|
||||||
|
expected = "homer"
|
||||||
|
actual = wn.GetName()
|
||||||
|
if expected != actual {
|
||||||
|
t.Fatalf("expected '%s', got '%s'", expected, actual)
|
||||||
|
}
|
||||||
|
actualMap := wn.GetLabels()
|
||||||
|
v, ok := actualMap["fruit"]
|
||||||
|
if !ok || v != "apple" {
|
||||||
|
t.Fatalf("unexpected labels '%v'", actualMap)
|
||||||
|
}
|
||||||
|
actualMap = wn.GetAnnotations()
|
||||||
|
v, ok = actualMap["greeting"]
|
||||||
|
if !ok || v != "Take me to your leader." {
|
||||||
|
t.Fatalf("unexpected annotations '%v'", actualMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,11 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KunstructuredFactoryImpl hides construction using apimachinery types.
|
// KunstructuredFactoryImpl makes instances of UnstructAdapter.
|
||||||
|
// These instances in turn adapt structs in
|
||||||
|
// k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
|
||||||
|
// to implement ifc.Kunstructured.
|
||||||
|
// This factory is meant to implement ifc.KunstructuredFactory.
|
||||||
type KunstructuredFactoryImpl struct {
|
type KunstructuredFactoryImpl struct {
|
||||||
hasher *kustHash
|
hasher *kustHash
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,14 @@ import (
|
|||||||
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
"k8s.io/apimachinery/pkg/util/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KustValidator validates Labels and annotations by apimachinery
|
// KustValidator validates Labels and annotations by apimachinery
|
||||||
type KustValidator struct{}
|
type KustValidator struct{}
|
||||||
|
|
||||||
|
var _ ifc.Validator = (*KustValidator)(nil)
|
||||||
|
|
||||||
// NewKustValidator returns a KustValidator object
|
// NewKustValidator returns a KustValidator object
|
||||||
func NewKustValidator() *KustValidator {
|
func NewKustValidator() *KustValidator {
|
||||||
return &KustValidator{}
|
return &KustValidator{}
|
||||||
@@ -8,6 +8,9 @@ const (
|
|||||||
namespace:
|
namespace:
|
||||||
- path: metadata/namespace
|
- path: metadata/namespace
|
||||||
create: true
|
create: true
|
||||||
|
- path: metadata/name
|
||||||
|
kind: Namespace
|
||||||
|
create: true
|
||||||
- path: subjects
|
- path: subjects
|
||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
- path: subjects
|
- path: subjects
|
||||||
|
|||||||
183
api/krusty/internal/provider/depprovider.go
Normal file
183
api/krusty/internal/provider/depprovider.go
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/k8sdeps/merge"
|
||||||
|
kmerge "sigs.k8s.io/kustomize/api/internal/merge"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/validate"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/wrappy"
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/validator"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DepProvider is a dependency provider.
|
||||||
|
//
|
||||||
|
// The instances it returns are either
|
||||||
|
// - old implementations backed by k8sdeps code,
|
||||||
|
// - new implementations backed by kyaml code.
|
||||||
|
//
|
||||||
|
// History:
|
||||||
|
//
|
||||||
|
// kubectl depends on k8s.io code, and at the time of writing, so
|
||||||
|
// does kustomize. Code that imports k8s.io/api* cannot be imported
|
||||||
|
// back into k8s.io/*, yet kustomize appears inside k8s.io/kubectl.
|
||||||
|
//
|
||||||
|
// To allow kustomize to appear inside kubectl, yet still be developed
|
||||||
|
// outside kubectl, the kustomize code was divided into the following
|
||||||
|
// packages
|
||||||
|
//
|
||||||
|
// api/
|
||||||
|
// k8sdeps/ (and internal/ks8deps/)
|
||||||
|
// ifc/
|
||||||
|
// krusty/
|
||||||
|
// everythingElse/
|
||||||
|
//
|
||||||
|
// with the following rules:
|
||||||
|
//
|
||||||
|
// - Only k8sdeps/ may import k8s.io/api*.
|
||||||
|
//
|
||||||
|
// - Only krusty/ (and its internals) may import k8sdeps/.
|
||||||
|
// I.e., ifc/ and everythingElse/ must not
|
||||||
|
// import k8sdeps/ or k8s.io/api*.
|
||||||
|
//
|
||||||
|
// - Code in krusty/ may use code in k8sdeps/ to create
|
||||||
|
// objects then inject said objects into
|
||||||
|
// everythingElse/ behind dependency neutral interfaces.
|
||||||
|
//
|
||||||
|
// The idea was to periodically copy, not import, the large k8sdeps/
|
||||||
|
// tree (plus a snippet from krusty/kustomizer.go) into the kubectl
|
||||||
|
// codebase via a large PR, and have kubectl depend on the rest via
|
||||||
|
// normal importing.
|
||||||
|
//
|
||||||
|
// Over 2019, however, kubectl underwent large changes including
|
||||||
|
// a switch to Go modules, and a concerted attempt to extract kubectl
|
||||||
|
// from the k8s repo. This made large kustomize integration PRs too
|
||||||
|
// intrusive to review.
|
||||||
|
//
|
||||||
|
// In 2020, kubectl is based on Go modules, and almost entirely
|
||||||
|
// extracted from the k8s.io repositories, and further the kyaml
|
||||||
|
// library has a appeared as a viable replacement to k8s.io/api*
|
||||||
|
// KRM manipulation code.
|
||||||
|
//
|
||||||
|
// The new plan is to eliminate k8sdeps/ entirely, along with its
|
||||||
|
// k8s.io/api* dependence, allowing kustomize code to be imported
|
||||||
|
// into kubectl via normal Go module imports. Then the kustomize API
|
||||||
|
// code can then move into the github.com/kubernetes-sigs/cli-utils
|
||||||
|
// repo. The kustomize CLI in github.com/kubernetes-sigs/kustomize
|
||||||
|
// and the kubectl CLI can then both depend on the kustomize API.
|
||||||
|
//
|
||||||
|
// So, all code that depends on k8sdeps must go behind interfaces,
|
||||||
|
// and kustomize must be factored to choose the implementation.
|
||||||
|
//
|
||||||
|
// That problem has been reduced to three interfaces, each having
|
||||||
|
// two implementations. (1) is k8sdeps-based, (2) is kyaml-based.
|
||||||
|
//
|
||||||
|
// - ifc.Kunstructured
|
||||||
|
//
|
||||||
|
// 1) api/k8sdeps/kunstruct.UnstructAdapter
|
||||||
|
//
|
||||||
|
// This adapts structs in
|
||||||
|
// k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
|
||||||
|
// to ifc.Kunstructured.
|
||||||
|
//
|
||||||
|
// 2) api/wrappy.WNode
|
||||||
|
//
|
||||||
|
// This adapts sigs.k8s.io/kustomize/kyaml/yaml.RNode
|
||||||
|
// to ifc.Unstructured.
|
||||||
|
//
|
||||||
|
// At time of writing, implementation started.
|
||||||
|
// Further reducing the size of ifc.Kunstructed
|
||||||
|
// would really reduce the work
|
||||||
|
// (e.g. drop Vars, drop ReplacementTranformer).
|
||||||
|
//
|
||||||
|
// - resmap.Merginator
|
||||||
|
//
|
||||||
|
// 1) api/internal/k8sdeps/merge.Merginator
|
||||||
|
//
|
||||||
|
// Uses k8s.io/apimachinery/pkg/util/strategicpatch,
|
||||||
|
// apimachinery/pkg/util/mergepatch, etc. to merge
|
||||||
|
// resource.Resource instances.
|
||||||
|
//
|
||||||
|
// 2) api/internal/merge.Merginator
|
||||||
|
//
|
||||||
|
// At time of writing, this is unimplemented.
|
||||||
|
//
|
||||||
|
// - ifc.Validator
|
||||||
|
//
|
||||||
|
// 1) api/k8sdeps/validator.KustValidator
|
||||||
|
//
|
||||||
|
// Uses k8s.io/apimachinery/pkg/api/validation and
|
||||||
|
// friends to validate strings.
|
||||||
|
//
|
||||||
|
// 2) api/internal/validate.FieldValidator
|
||||||
|
//
|
||||||
|
// At time of writing, this is a do-nothing
|
||||||
|
// validator as it's not critical to kustomize function.
|
||||||
|
//
|
||||||
|
// Proposed plan:
|
||||||
|
// [ ] Ship kustomize with the ability to switch from 1 to 2 via
|
||||||
|
// an --enable_kyaml flag.
|
||||||
|
// [ ] Make --enable_kyaml true by default.
|
||||||
|
// [ ] When 2 is not noticeably more buggy than 1, delete 1.
|
||||||
|
// I.e. delete k8sdeps/, transitively deleting all k8s.io/api* deps.
|
||||||
|
// This DepProvider should be left in place to retain these
|
||||||
|
// comments, but it will have only one choice.
|
||||||
|
// [ ] The way is now clear to reintegrate into kubectl.
|
||||||
|
// This should be done ASAP; the last step is cleanup.
|
||||||
|
// [ ] With only one impl of Kunstructure remaining, that interface
|
||||||
|
// and WNode can be deleted, along with this DepProvider.
|
||||||
|
// The other two interfaces could be dropped too.
|
||||||
|
//
|
||||||
|
// When the above is done, kustomize will use yaml.RNode and/or
|
||||||
|
// KRM Config Functions directly and exclusively.
|
||||||
|
// If you're reading this, plan not done.
|
||||||
|
//
|
||||||
|
type DepProvider struct {
|
||||||
|
resourceFactory *resource.Factory
|
||||||
|
merginator resmap.Merginator
|
||||||
|
fieldValidator ifc.Validator
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeK8sdepBasedInstances() *DepProvider {
|
||||||
|
kf := kunstruct.NewKunstructuredFactoryImpl()
|
||||||
|
rf := resource.NewFactory(kf)
|
||||||
|
return &DepProvider{
|
||||||
|
resourceFactory: rf,
|
||||||
|
merginator: merge.NewMerginator(rf),
|
||||||
|
fieldValidator: validator.NewKustValidator(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeKyamlBasedInstances() *DepProvider {
|
||||||
|
kf := &wrappy.WNodeFactory{}
|
||||||
|
rf := resource.NewFactory(kf)
|
||||||
|
return &DepProvider{
|
||||||
|
resourceFactory: rf,
|
||||||
|
merginator: kmerge.NewMerginator(rf),
|
||||||
|
fieldValidator: validate.NewFieldValidator(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDepProvider(useKyaml bool) *DepProvider {
|
||||||
|
if useKyaml {
|
||||||
|
return makeKyamlBasedInstances()
|
||||||
|
}
|
||||||
|
return makeK8sdepBasedInstances()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dp *DepProvider) GetResourceFactory() *resource.Factory {
|
||||||
|
return dp.resourceFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dp *DepProvider) GetMerginator() resmap.Merginator {
|
||||||
|
return dp.merginator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dp *DepProvider) GetFieldValidator() ifc.Validator {
|
||||||
|
return dp.fieldValidator
|
||||||
|
}
|
||||||
@@ -8,16 +8,13 @@ import (
|
|||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/builtins"
|
"sigs.k8s.io/kustomize/api/builtins"
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/api/internal/k8sdeps/merge"
|
|
||||||
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||||
"sigs.k8s.io/kustomize/api/internal/target"
|
"sigs.k8s.io/kustomize/api/internal/target"
|
||||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
|
||||||
"sigs.k8s.io/kustomize/api/k8sdeps/validator"
|
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/krusty/internal/provider"
|
||||||
fLdr "sigs.k8s.io/kustomize/api/loader"
|
fLdr "sigs.k8s.io/kustomize/api/loader"
|
||||||
"sigs.k8s.io/kustomize/api/provenance"
|
"sigs.k8s.io/kustomize/api/provenance"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,13 +25,18 @@ import (
|
|||||||
// number of overlays and bases), then make a Kustomizer
|
// number of overlays and bases), then make a Kustomizer
|
||||||
// injected with the given fileystem, then call Run.
|
// injected with the given fileystem, then call Run.
|
||||||
type Kustomizer struct {
|
type Kustomizer struct {
|
||||||
fSys filesys.FileSystem
|
fSys filesys.FileSystem
|
||||||
options *Options
|
options *Options
|
||||||
|
depProvider *provider.DepProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeKustomizer returns an instance of Kustomizer.
|
// MakeKustomizer returns an instance of Kustomizer.
|
||||||
func MakeKustomizer(fSys filesys.FileSystem, o *Options) *Kustomizer {
|
func MakeKustomizer(fSys filesys.FileSystem, o *Options) *Kustomizer {
|
||||||
return &Kustomizer{fSys: fSys, options: o}
|
return &Kustomizer{
|
||||||
|
fSys: fSys,
|
||||||
|
options: o,
|
||||||
|
depProvider: provider.NewDepProvider(o.UseKyaml),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run performs a kustomization.
|
// Run performs a kustomization.
|
||||||
@@ -49,11 +51,9 @@ func MakeKustomizer(fSys filesys.FileSystem, o *Options) *Kustomizer {
|
|||||||
// on any number of internal paths (e.g. the filesystem may contain
|
// on any number of internal paths (e.g. the filesystem may contain
|
||||||
// multiple overlays, and Run can be called on each of them).
|
// multiple overlays, and Run can be called on each of them).
|
||||||
func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
|
func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
|
||||||
resourceFactory := resource.NewFactory(
|
|
||||||
kunstruct.NewKunstructuredFactoryImpl())
|
|
||||||
resmapFactory := resmap.NewFactory(
|
resmapFactory := resmap.NewFactory(
|
||||||
resourceFactory,
|
b.depProvider.GetResourceFactory(),
|
||||||
merge.NewMerginator(resourceFactory))
|
b.depProvider.GetMerginator())
|
||||||
lr := fLdr.RestrictionNone
|
lr := fLdr.RestrictionNone
|
||||||
if b.options.LoadRestrictions == types.LoadRestrictionsRootOnly {
|
if b.options.LoadRestrictions == types.LoadRestrictionsRootOnly {
|
||||||
lr = fLdr.RestrictionRootOnly
|
lr = fLdr.RestrictionRootOnly
|
||||||
@@ -65,7 +65,7 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
|
|||||||
defer ldr.Cleanup()
|
defer ldr.Cleanup()
|
||||||
kt := target.NewKustTarget(
|
kt := target.NewKustTarget(
|
||||||
ldr,
|
ldr,
|
||||||
validator.NewKustValidator(),
|
b.depProvider.GetFieldValidator(),
|
||||||
resmapFactory,
|
resmapFactory,
|
||||||
pLdr.NewLoader(b.options.PluginConfig, resmapFactory),
|
pLdr.NewLoader(b.options.PluginConfig, resmapFactory),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -104,10 +104,10 @@ spec:
|
|||||||
- valueFrom:
|
- valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
key: MYSQL_DATABASE
|
key: MYSQL_DATABASE
|
||||||
name: mysql
|
name: mysql-9792mdchtg
|
||||||
envFrom:
|
envFrom:
|
||||||
- configMapRef:
|
- configMapRef:
|
||||||
name: mysql
|
name: mysql-9792mdchtg
|
||||||
name: handler
|
name: handler
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNullValues(t *testing.T) {
|
func TestNullValues1(t *testing.T) {
|
||||||
th := kusttest_test.MakeHarness(t)
|
th := kusttest_test.MakeHarness(t)
|
||||||
th.WriteF("/app/deployment.yaml", `
|
th.WriteF("/app/deployment.yaml", `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
@@ -62,3 +62,36 @@ spec:
|
|||||||
name: example
|
name: example
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNullValues2(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteF("deploy.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
volumes: null
|
||||||
|
`)
|
||||||
|
th.WriteK(".", `
|
||||||
|
resources:
|
||||||
|
- deploy.yaml
|
||||||
|
`)
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: test
|
||||||
|
volumes: null
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,14 +32,20 @@ type Options struct {
|
|||||||
|
|
||||||
// Options related to kustomize plugins.
|
// Options related to kustomize plugins.
|
||||||
PluginConfig *types.PluginConfig
|
PluginConfig *types.PluginConfig
|
||||||
|
|
||||||
|
// When true, use kyaml/ packages to manipulate KRM yaml.
|
||||||
|
// When false, use k8sdeps/ instead (uses k8s.io/api* packages).
|
||||||
|
UseKyaml bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeDefaultOptions returns a default instance of Options.
|
// MakeDefaultOptions returns a default instance of Options.
|
||||||
func MakeDefaultOptions() *Options {
|
func MakeDefaultOptions() *Options {
|
||||||
return &Options{
|
return &Options{
|
||||||
DoLegacyResourceSort: true,
|
DoLegacyResourceSort: false,
|
||||||
|
AddManagedbyLabel: false,
|
||||||
LoadRestrictions: types.LoadRestrictionsRootOnly,
|
LoadRestrictions: types.LoadRestrictionsRootOnly,
|
||||||
DoPrune: false,
|
DoPrune: false,
|
||||||
PluginConfig: konfig.DisabledPluginConfig(),
|
PluginConfig: konfig.DisabledPluginConfig(),
|
||||||
|
UseKyaml: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,25 +215,25 @@ spec2:
|
|||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: nginx:v1
|
- image: nginx:v2
|
||||||
name: nginx3
|
name: nginx3
|
||||||
- image: my-nginx:latest
|
- image: my-nginx:previous
|
||||||
name: nginx4
|
name: nginx4
|
||||||
spec3:
|
spec3:
|
||||||
template:
|
template:
|
||||||
spec:
|
spec:
|
||||||
initContainers:
|
initContainers:
|
||||||
- image: postgres:alpine-9
|
- image: my-postgres:v3
|
||||||
name: postgresdb
|
name: postgresdb
|
||||||
- image: docker:17-git
|
- image: my-docker@sha256:25a0d4b4
|
||||||
name: init-docker
|
name: init-docker
|
||||||
- image: myprivaterepohostname:1234/my/image:latest
|
- image: myprivaterepohostname:1234/my/image:v1.0.1
|
||||||
name: myImage
|
name: myImage
|
||||||
- image: myprivaterepohostname:1234/my/image
|
- image: myprivaterepohostname:1234/my/image:v1.0.1
|
||||||
name: myImage2
|
name: myImage2
|
||||||
- image: my-app-image:v1
|
- image: my-app-image:v1
|
||||||
name: my-app
|
name: my-app
|
||||||
- image: gcr.io:8080/my-project/my-cool-app:latest
|
- image: my-cool-app:latest
|
||||||
name: my-cool-app
|
name: my-cool-app
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ type remoteTargetSpec struct {
|
|||||||
|
|
||||||
// Dir is where the resource is saved
|
// Dir is where the resource is saved
|
||||||
Dir filesys.ConfirmedDir
|
Dir filesys.ConfirmedDir
|
||||||
|
|
||||||
|
// TempDir is the directory created to hold all resources, including Dir
|
||||||
|
TempDir filesys.ConfirmedDir
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getter is a function that can gets resource
|
// Getter is a function that can gets resource
|
||||||
@@ -31,7 +34,7 @@ func newLoaderAtGetter(raw string, fSys filesys.FileSystem, referrer *fileLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
cleaner := func() error {
|
cleaner := func() error {
|
||||||
return fSys.RemoveAll(rs.Dir.String())
|
return fSys.RemoveAll(rs.TempDir.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := getter(rs); err != nil {
|
if err := getter(rs); err != nil {
|
||||||
@@ -55,12 +58,12 @@ func newLoaderAtGetter(raw string, fSys filesys.FileSystem, referrer *fileLoader
|
|||||||
func getRemoteTarget(rs *remoteTargetSpec) error {
|
func getRemoteTarget(rs *remoteTargetSpec) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
rs.Dir, err = filesys.NewTmpConfirmedDir()
|
rs.TempDir, err = filesys.NewTmpConfirmedDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rs.Dir = filesys.ConfirmedDir(rs.Dir.Join("repo"))
|
rs.Dir = filesys.ConfirmedDir(rs.TempDir.Join("repo"))
|
||||||
|
|
||||||
// Get the pwd
|
// Get the pwd
|
||||||
pwd, err := os.Getwd()
|
pwd, err := os.Getwd()
|
||||||
|
|||||||
@@ -11,10 +11,19 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Merginator merges resources.
|
||||||
|
type Merginator interface {
|
||||||
|
// Merge creates a new ResMap by merging incoming resources.
|
||||||
|
// Error if conflict found.
|
||||||
|
Merge([]*resource.Resource) (ResMap, error)
|
||||||
|
}
|
||||||
|
|
||||||
// Factory makes instances of ResMap.
|
// Factory makes instances of ResMap.
|
||||||
type Factory struct {
|
type Factory struct {
|
||||||
|
// Makes resources.
|
||||||
resF *resource.Factory
|
resF *resource.Factory
|
||||||
pm Merginator
|
// Makes ResMaps via merging.
|
||||||
|
pm Merginator
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFactory returns a new resmap.Factory.
|
// NewFactory returns a new resmap.Factory.
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package resmap
|
|
||||||
|
|
||||||
import "sigs.k8s.io/kustomize/api/resource"
|
|
||||||
|
|
||||||
// Merginator merges resources.
|
|
||||||
type Merginator interface {
|
|
||||||
// Merge creates a new ResMap by merging incoming resources.
|
|
||||||
// Error if conflict found.
|
|
||||||
Merge([]*resource.Resource) (ResMap, error)
|
|
||||||
}
|
|
||||||
@@ -6,16 +6,10 @@
|
|||||||
package resmap
|
package resmap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
"sigs.k8s.io/kustomize/api/resid"
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/yaml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Transformer modifies an instance of ResMap.
|
// A Transformer modifies an instance of ResMap.
|
||||||
@@ -242,569 +236,3 @@ type ResMap interface {
|
|||||||
// are selected by a Selector
|
// are selected by a Selector
|
||||||
Select(types.Selector) ([]*resource.Resource, error)
|
Select(types.Selector) ([]*resource.Resource, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// resWrangler holds the content manipulated by kustomize.
|
|
||||||
type resWrangler struct {
|
|
||||||
// Resource list maintained in load (append) order.
|
|
||||||
// This is important for transformers, which must
|
|
||||||
// be performed in a specific order, and for users
|
|
||||||
// who for whatever reasons wish the order they
|
|
||||||
// specify in kustomizations to be maintained and
|
|
||||||
// available as an option for final YAML rendering.
|
|
||||||
rList []*resource.Resource
|
|
||||||
}
|
|
||||||
|
|
||||||
func newOne() *resWrangler {
|
|
||||||
result := &resWrangler{}
|
|
||||||
result.Clear()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear implements ResMap.
|
|
||||||
func (m *resWrangler) Clear() {
|
|
||||||
m.rList = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size implements ResMap.
|
|
||||||
func (m *resWrangler) Size() int {
|
|
||||||
return len(m.rList)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *resWrangler) indexOfResource(other *resource.Resource) int {
|
|
||||||
for i, r := range m.rList {
|
|
||||||
if r == other {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resources implements ResMap.
|
|
||||||
func (m *resWrangler) Resources() []*resource.Resource {
|
|
||||||
tmp := make([]*resource.Resource, len(m.rList))
|
|
||||||
copy(tmp, m.rList)
|
|
||||||
return tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append implements ResMap.
|
|
||||||
func (m *resWrangler) Append(res *resource.Resource) error {
|
|
||||||
id := res.CurId()
|
|
||||||
if r := m.GetMatchingResourcesByCurrentId(id.Equals); len(r) > 0 {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"may not add resource with an already registered id: %s", id)
|
|
||||||
}
|
|
||||||
m.rList = append(m.rList, res)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove implements ResMap.
|
|
||||||
func (m *resWrangler) Remove(adios resid.ResId) error {
|
|
||||||
tmp := newOne()
|
|
||||||
for _, r := range m.rList {
|
|
||||||
if r.CurId() != adios {
|
|
||||||
tmp.Append(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if tmp.Size() != m.Size()-1 {
|
|
||||||
return fmt.Errorf("id %s not found in removal", adios)
|
|
||||||
}
|
|
||||||
m.rList = tmp.rList
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace implements ResMap.
|
|
||||||
func (m *resWrangler) Replace(res *resource.Resource) (int, error) {
|
|
||||||
id := res.CurId()
|
|
||||||
i, err := m.GetIndexOfCurrentId(id)
|
|
||||||
if err != nil {
|
|
||||||
return -1, errors.Wrap(err, "in Replace")
|
|
||||||
}
|
|
||||||
if i < 0 {
|
|
||||||
return -1, fmt.Errorf("cannot find resource with id %s to replace", id)
|
|
||||||
}
|
|
||||||
m.rList[i] = res
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllIds implements ResMap.
|
|
||||||
func (m *resWrangler) AllIds() (ids []resid.ResId) {
|
|
||||||
ids = make([]resid.ResId, m.Size())
|
|
||||||
for i, r := range m.rList {
|
|
||||||
ids[i] = r.CurId()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug implements ResMap.
|
|
||||||
func (m *resWrangler) Debug(title string) {
|
|
||||||
fmt.Println("--------------------------- " + title)
|
|
||||||
firstObj := true
|
|
||||||
for i, r := range m.rList {
|
|
||||||
if firstObj {
|
|
||||||
firstObj = false
|
|
||||||
} else {
|
|
||||||
fmt.Println("---")
|
|
||||||
}
|
|
||||||
fmt.Printf("# %d %s\n", i, r.OrgId())
|
|
||||||
blob, err := yaml.Marshal(r.Map())
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Println(string(blob))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type IdMatcher func(resid.ResId) bool
|
|
||||||
|
|
||||||
// GetByIndex implements ResMap.
|
|
||||||
func (m *resWrangler) GetByIndex(i int) *resource.Resource {
|
|
||||||
if i < 0 || i >= m.Size() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return m.rList[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetIndexOfCurrentId implements ResMap.
|
|
||||||
func (m *resWrangler) GetIndexOfCurrentId(id resid.ResId) (int, error) {
|
|
||||||
count := 0
|
|
||||||
result := -1
|
|
||||||
for i, r := range m.rList {
|
|
||||||
if id.Equals(r.CurId()) {
|
|
||||||
count++
|
|
||||||
result = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if count > 1 {
|
|
||||||
return -1, fmt.Errorf("id matched %d resources", count)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type IdFromResource func(r *resource.Resource) resid.ResId
|
|
||||||
|
|
||||||
func GetOriginalId(r *resource.Resource) resid.ResId { return r.OrgId() }
|
|
||||||
func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() }
|
|
||||||
|
|
||||||
// GetMatchingResourcesByCurrentId implements ResMap.
|
|
||||||
func (m *resWrangler) GetMatchingResourcesByCurrentId(
|
|
||||||
matches IdMatcher) []*resource.Resource {
|
|
||||||
return m.filteredById(matches, GetCurrentId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMatchingResourcesByOriginalId implements ResMap.
|
|
||||||
func (m *resWrangler) GetMatchingResourcesByOriginalId(
|
|
||||||
matches IdMatcher) []*resource.Resource {
|
|
||||||
return m.filteredById(matches, GetOriginalId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *resWrangler) filteredById(
|
|
||||||
matches IdMatcher, idGetter IdFromResource) []*resource.Resource {
|
|
||||||
var result []*resource.Resource
|
|
||||||
for _, r := range m.rList {
|
|
||||||
if matches(idGetter(r)) {
|
|
||||||
result = append(result, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByCurrentId implements ResMap.
|
|
||||||
func (m *resWrangler) GetByCurrentId(
|
|
||||||
id resid.ResId) (*resource.Resource, error) {
|
|
||||||
return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetByOriginalId implements ResMap.
|
|
||||||
func (m *resWrangler) GetByOriginalId(
|
|
||||||
id resid.ResId) (*resource.Resource, error) {
|
|
||||||
return demandOneMatch(m.GetMatchingResourcesByOriginalId, id, "Original")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetById implements ResMap.
|
|
||||||
func (m *resWrangler) GetById(
|
|
||||||
id resid.ResId) (*resource.Resource, error) {
|
|
||||||
match, err1 := m.GetByOriginalId(id)
|
|
||||||
if err1 == nil {
|
|
||||||
return match, nil
|
|
||||||
}
|
|
||||||
match, err2 := m.GetByCurrentId(id)
|
|
||||||
if err2 == nil {
|
|
||||||
return match, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"%s; %s; failed to find unique target for patch %s",
|
|
||||||
err1.Error(), err2.Error(), id.GvknString())
|
|
||||||
}
|
|
||||||
|
|
||||||
type resFinder func(IdMatcher) []*resource.Resource
|
|
||||||
|
|
||||||
func demandOneMatch(
|
|
||||||
f resFinder, id resid.ResId, s string) (*resource.Resource, error) {
|
|
||||||
r := f(id.Equals)
|
|
||||||
if len(r) == 1 {
|
|
||||||
return r[0], nil
|
|
||||||
}
|
|
||||||
if len(r) > 1 {
|
|
||||||
return nil, fmt.Errorf("multiple matches for %sId %s", s, id)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("no matches for %sId %s", s, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GroupedByCurrentNamespace implements ResMap.GroupByCurrentNamespace
|
|
||||||
func (m *resWrangler) GroupedByCurrentNamespace() map[string][]*resource.Resource {
|
|
||||||
items := m.groupedByCurrentNamespace()
|
|
||||||
delete(items, resid.TotallyNotANamespace)
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
|
||||||
// NonNamespaceable implements ResMap.NonNamespaceable
|
|
||||||
func (m *resWrangler) NonNamespaceable() []*resource.Resource {
|
|
||||||
return m.groupedByCurrentNamespace()[resid.TotallyNotANamespace]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *resWrangler) groupedByCurrentNamespace() map[string][]*resource.Resource {
|
|
||||||
byNamespace := make(map[string][]*resource.Resource)
|
|
||||||
for _, res := range m.rList {
|
|
||||||
namespace := res.CurId().EffectiveNamespace()
|
|
||||||
if _, found := byNamespace[namespace]; !found {
|
|
||||||
byNamespace[namespace] = []*resource.Resource{}
|
|
||||||
}
|
|
||||||
byNamespace[namespace] = append(byNamespace[namespace], res)
|
|
||||||
}
|
|
||||||
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.
|
|
||||||
func (m *resWrangler) AsYaml() ([]byte, error) {
|
|
||||||
firstObj := true
|
|
||||||
var b []byte
|
|
||||||
buf := bytes.NewBuffer(b)
|
|
||||||
for _, res := range m.Resources() {
|
|
||||||
out, err := yaml.Marshal(res.Map())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if firstObj {
|
|
||||||
firstObj = false
|
|
||||||
} else {
|
|
||||||
if _, err = buf.WriteString("---\n"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err = buf.Write(out); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorIfNotEqualSets implements ResMap.
|
|
||||||
func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error {
|
|
||||||
m2, ok := other.(*resWrangler)
|
|
||||||
if !ok {
|
|
||||||
panic("bad cast")
|
|
||||||
}
|
|
||||||
if m.Size() != m2.Size() {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"lists have different number of entries: %#v doesn't equal %#v",
|
|
||||||
m.rList, m2.rList)
|
|
||||||
}
|
|
||||||
seen := make(map[int]bool)
|
|
||||||
for _, r1 := range m.rList {
|
|
||||||
id := r1.CurId()
|
|
||||||
others := m2.GetMatchingResourcesByCurrentId(id.Equals)
|
|
||||||
if len(others) == 0 {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"id in self missing from other; id: %s", id)
|
|
||||||
}
|
|
||||||
if len(others) > 1 {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"id in self matches %d in other; id: %s", len(others), id)
|
|
||||||
}
|
|
||||||
r2 := others[0]
|
|
||||||
if !r1.KunstructEqual(r2) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"kunstruct not equal: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
|
|
||||||
r1, r2, r1, r2)
|
|
||||||
}
|
|
||||||
seen[m2.indexOfResource(r2)] = true
|
|
||||||
}
|
|
||||||
if len(seen) != m.Size() {
|
|
||||||
return fmt.Errorf("counting problem %d != %d", len(seen), m.Size())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorIfNotEqualList implements ResMap.
|
|
||||||
func (m *resWrangler) ErrorIfNotEqualLists(other ResMap) error {
|
|
||||||
m2, ok := other.(*resWrangler)
|
|
||||||
if !ok {
|
|
||||||
panic("bad cast")
|
|
||||||
}
|
|
||||||
if m.Size() != m2.Size() {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"lists have different number of entries: %#v doesn't equal %#v",
|
|
||||||
m.rList, m2.rList)
|
|
||||||
}
|
|
||||||
for i, r1 := range m.rList {
|
|
||||||
r2 := m2.rList[i]
|
|
||||||
if !r1.Equals(r2) {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"Item i=%d differs:\n n1 = %s\n n2 = %s\n o1 = %s\n o2 = %s\n",
|
|
||||||
i, r1.OrgId(), r2.OrgId(), r1, r2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type resCopier func(r *resource.Resource) *resource.Resource
|
|
||||||
|
|
||||||
// ShallowCopy implements ResMap.
|
|
||||||
func (m *resWrangler) ShallowCopy() ResMap {
|
|
||||||
return m.makeCopy(
|
|
||||||
func(r *resource.Resource) *resource.Resource {
|
|
||||||
return r
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy implements ResMap.
|
|
||||||
func (m *resWrangler) DeepCopy() ResMap {
|
|
||||||
return m.makeCopy(
|
|
||||||
func(r *resource.Resource) *resource.Resource {
|
|
||||||
return r.DeepCopy()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeCopy copies the ResMap.
|
|
||||||
func (m *resWrangler) makeCopy(copier resCopier) ResMap {
|
|
||||||
result := &resWrangler{}
|
|
||||||
result.rList = make([]*resource.Resource, m.Size())
|
|
||||||
for i, r := range m.rList {
|
|
||||||
result.rList[i] = copier(r)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubsetThatCouldBeReferencedByResource implements ResMap.
|
|
||||||
func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
|
|
||||||
inputRes *resource.Resource) ResMap {
|
|
||||||
result := newOne()
|
|
||||||
inputId := inputRes.CurId()
|
|
||||||
isInputIdNamespaceable := inputId.IsNamespaceableKind()
|
|
||||||
rctxm := inputRes.PrefixesSuffixesEquals
|
|
||||||
subjectNamespaces := getNamespacesForRoleBinding(inputRes)
|
|
||||||
for _, r := range m.Resources() {
|
|
||||||
// Need to match more accuratly both at the time of selection and transformation.
|
|
||||||
// OutmostPrefixSuffixEquals is not accurate enough since it is only using
|
|
||||||
// the outer most suffix and the last prefix. Use PrefixedSuffixesEquals instead.
|
|
||||||
resId := r.CurId()
|
|
||||||
if (!isInputIdNamespaceable || !resId.IsNamespaceableKind() || resId.IsNsEquals(inputId) ||
|
|
||||||
isRoleBindingNamespace(&subjectNamespaces, r.GetNamespace())) && r.InSameKustomizeCtx(rctxm) {
|
|
||||||
result.append(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// isRoleBindingNamespace returns true is the namespace `ns` is in role binding
|
|
||||||
// namespaces `m`
|
|
||||||
func isRoleBindingNamespace(m *map[string]bool, ns string) bool {
|
|
||||||
return (*m)[ns]
|
|
||||||
}
|
|
||||||
|
|
||||||
// getNamespacesForRoleBinding returns referenced ServiceAccount namespaces if the inputRes is
|
|
||||||
// a RoleBinding
|
|
||||||
func getNamespacesForRoleBinding(inputRes *resource.Resource) map[string]bool {
|
|
||||||
res := make(map[string]bool)
|
|
||||||
if inputRes.GetKind() != "RoleBinding" {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
subjects, err := inputRes.GetSlice("subjects")
|
|
||||||
if err != nil || subjects == nil {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, s := range subjects {
|
|
||||||
subject := s.(map[string]interface{})
|
|
||||||
if subject["namespace"] == nil || subject["kind"] == nil ||
|
|
||||||
subject["kind"].(string) != "ServiceAccount" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
res[subject["namespace"].(string)] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *resWrangler) append(res *resource.Resource) {
|
|
||||||
m.rList = append(m.rList, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendAll implements ResMap.
|
|
||||||
func (m *resWrangler) AppendAll(other ResMap) error {
|
|
||||||
if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, res := range other.Resources() {
|
|
||||||
if err := m.Append(res); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AbsorbAll implements ResMap.
|
|
||||||
func (m *resWrangler) AbsorbAll(other ResMap) error {
|
|
||||||
if other == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, r := range other.Resources() {
|
|
||||||
err := m.appendReplaceOrMerge(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *resWrangler) appendReplaceOrMerge(
|
|
||||||
res *resource.Resource) error {
|
|
||||||
id := res.CurId()
|
|
||||||
matches := m.GetMatchingResourcesByOriginalId(id.Equals)
|
|
||||||
if len(matches) == 0 {
|
|
||||||
matches = m.GetMatchingResourcesByCurrentId(id.Equals)
|
|
||||||
}
|
|
||||||
switch len(matches) {
|
|
||||||
case 0:
|
|
||||||
switch res.Behavior() {
|
|
||||||
case types.BehaviorMerge, types.BehaviorReplace:
|
|
||||||
return fmt.Errorf(
|
|
||||||
"id %#v does not exist; cannot merge or replace", id)
|
|
||||||
default:
|
|
||||||
// presumably types.BehaviorCreate
|
|
||||||
err := m.Append(res)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
old := matches[0]
|
|
||||||
if old == nil {
|
|
||||||
return fmt.Errorf("id lookup failure")
|
|
||||||
}
|
|
||||||
index := m.indexOfResource(old)
|
|
||||||
if index < 0 {
|
|
||||||
return fmt.Errorf("indexing problem")
|
|
||||||
}
|
|
||||||
switch res.Behavior() {
|
|
||||||
case types.BehaviorReplace:
|
|
||||||
res.Replace(old)
|
|
||||||
case types.BehaviorMerge:
|
|
||||||
res.Merge(old)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf(
|
|
||||||
"id %#v exists; behavior must be merge or replace", id)
|
|
||||||
}
|
|
||||||
i, err := m.Replace(res)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if i != index {
|
|
||||||
return fmt.Errorf("unexpected index in replacement")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf(
|
|
||||||
"found multiple objects %v that could accept merge of %v",
|
|
||||||
matches, id)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func anchorRegex(pattern string) string {
|
|
||||||
if pattern == "" {
|
|
||||||
return pattern
|
|
||||||
}
|
|
||||||
return "^" + pattern + "$"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select returns a list of resources that
|
|
||||||
// are selected by a Selector
|
|
||||||
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
|
|
||||||
ns := regexp.MustCompile(anchorRegex(s.Namespace))
|
|
||||||
nm := regexp.MustCompile(anchorRegex(s.Name))
|
|
||||||
var result []*resource.Resource
|
|
||||||
for _, r := range m.Resources() {
|
|
||||||
curId := r.CurId()
|
|
||||||
orgId := r.OrgId()
|
|
||||||
|
|
||||||
// matches the namespace when namespace is not empty in the selector
|
|
||||||
// It first tries to match with the original namespace
|
|
||||||
// then matches with the current namespace
|
|
||||||
if r.GetNamespace() != "" {
|
|
||||||
matched := ns.MatchString(orgId.EffectiveNamespace())
|
|
||||||
if !matched {
|
|
||||||
matched = ns.MatchString(curId.EffectiveNamespace())
|
|
||||||
if !matched {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches the name when name is not empty in the selector
|
|
||||||
// It first tries to match with the original name
|
|
||||||
// then matches with the current name
|
|
||||||
if r.GetName() != "" {
|
|
||||||
matched := nm.MatchString(orgId.Name)
|
|
||||||
if !matched {
|
|
||||||
matched = nm.MatchString(curId.Name)
|
|
||||||
if !matched {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches the GVK
|
|
||||||
if !r.GetGvk().IsSelected(&s.Gvk) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches the label selector
|
|
||||||
matched, err := r.MatchesLabelSelector(s.LabelSelector)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !matched {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches the annotation selector
|
|
||||||
matched, err = r.MatchesAnnotationSelector(s.AnnotationSelector)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !matched {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result = append(result, r)
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,899 +3,4 @@
|
|||||||
|
|
||||||
package resmap_test
|
package resmap_test
|
||||||
|
|
||||||
import (
|
// See reswrangler_test.go
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
|
||||||
"sigs.k8s.io/kustomize/api/resid"
|
|
||||||
. "sigs.k8s.io/kustomize/api/resmap"
|
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
|
||||||
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rf = resource.NewFactory(
|
|
||||||
kunstruct.NewKunstructuredFactoryImpl())
|
|
||||||
var rmF = NewFactory(rf, nil)
|
|
||||||
|
|
||||||
func doAppend(t *testing.T, w ResMap, r *resource.Resource) {
|
|
||||||
err := w.Append(r)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("append error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func doRemove(t *testing.T, w ResMap, id resid.ResId) {
|
|
||||||
err := w.Remove(id)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("remove error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a resource with a predictable name.
|
|
||||||
func makeCm(i int) *resource.Resource {
|
|
||||||
return rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": fmt.Sprintf("cm%03d", i),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maintain the class invariant that no two
|
|
||||||
// resources can have the same CurId().
|
|
||||||
func TestAppendRejectsDuplicateResId(t *testing.T) {
|
|
||||||
w := New()
|
|
||||||
if err := w.Append(makeCm(1)); err != nil {
|
|
||||||
t.Fatalf("append error: %v", err)
|
|
||||||
}
|
|
||||||
err := w.Append(makeCm(1))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected append error")
|
|
||||||
}
|
|
||||||
if !strings.Contains(
|
|
||||||
err.Error(),
|
|
||||||
"may not add resource with an already registered id") {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendRemove(t *testing.T) {
|
|
||||||
w1 := New()
|
|
||||||
doAppend(t, w1, makeCm(1))
|
|
||||||
doAppend(t, w1, makeCm(2))
|
|
||||||
doAppend(t, w1, makeCm(3))
|
|
||||||
doAppend(t, w1, makeCm(4))
|
|
||||||
doAppend(t, w1, makeCm(5))
|
|
||||||
doAppend(t, w1, makeCm(6))
|
|
||||||
doAppend(t, w1, makeCm(7))
|
|
||||||
doRemove(t, w1, makeCm(1).OrgId())
|
|
||||||
doRemove(t, w1, makeCm(3).OrgId())
|
|
||||||
doRemove(t, w1, makeCm(5).OrgId())
|
|
||||||
doRemove(t, w1, makeCm(7).OrgId())
|
|
||||||
|
|
||||||
w2 := New()
|
|
||||||
doAppend(t, w2, makeCm(2))
|
|
||||||
doAppend(t, w2, makeCm(4))
|
|
||||||
doAppend(t, w2, makeCm(6))
|
|
||||||
if !reflect.DeepEqual(w1, w1) {
|
|
||||||
w1.Debug("w1")
|
|
||||||
w2.Debug("w2")
|
|
||||||
t.Fatalf("mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := w2.Append(makeCm(6))
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemove(t *testing.T) {
|
|
||||||
w := New()
|
|
||||||
r := makeCm(1)
|
|
||||||
err := w.Remove(r.OrgId())
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error")
|
|
||||||
}
|
|
||||||
err = w.Append(r)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
err = w.Remove(r.OrgId())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
err = w.Remove(r.OrgId())
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace(t *testing.T) {
|
|
||||||
cm5 := makeCm(5)
|
|
||||||
cm700 := makeCm(700)
|
|
||||||
otherCm5 := makeCm(5)
|
|
||||||
|
|
||||||
w := New()
|
|
||||||
doAppend(t, w, makeCm(1))
|
|
||||||
doAppend(t, w, makeCm(2))
|
|
||||||
doAppend(t, w, makeCm(3))
|
|
||||||
doAppend(t, w, makeCm(4))
|
|
||||||
doAppend(t, w, cm5)
|
|
||||||
doAppend(t, w, makeCm(6))
|
|
||||||
doAppend(t, w, makeCm(7))
|
|
||||||
|
|
||||||
oldSize := w.Size()
|
|
||||||
_, err := w.Replace(otherCm5)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if w.Size() != oldSize {
|
|
||||||
t.Fatalf("unexpected size %d", w.Size())
|
|
||||||
}
|
|
||||||
if r, err := w.GetByCurrentId(cm5.OrgId()); err != nil || r != otherCm5 {
|
|
||||||
t.Fatalf("unexpected result r=%s, err=%v", r.CurId(), err)
|
|
||||||
}
|
|
||||||
if err := w.Append(cm5); err == nil {
|
|
||||||
t.Fatalf("expected id already there error")
|
|
||||||
}
|
|
||||||
if err := w.Remove(cm5.OrgId()); err != nil {
|
|
||||||
t.Fatalf("unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if err := w.Append(cm700); err != nil {
|
|
||||||
t.Fatalf("unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if err := w.Append(cm5); err != nil {
|
|
||||||
t.Fatalf("unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncodeAsYaml(t *testing.T) {
|
|
||||||
encoded := []byte(`apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm1
|
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: cm2
|
|
||||||
`)
|
|
||||||
input := resmaptest_test.NewRmBuilder(t, rf).Add(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm1",
|
|
||||||
},
|
|
||||||
}).Add(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm2",
|
|
||||||
},
|
|
||||||
}).ResMap()
|
|
||||||
out, err := input.AsYaml()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(out, encoded) {
|
|
||||||
t.Fatalf("%s doesn't match expected %s", out, encoded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetMatchingResourcesByCurrentId(t *testing.T) {
|
|
||||||
r1 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "alice",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r2 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "bob",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r3 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "bob",
|
|
||||||
"namespace": "happy",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r4 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "charlie",
|
|
||||||
"namespace": "happy",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r5 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "charlie",
|
|
||||||
"namespace": "happy",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
m := resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).ResMap()
|
|
||||||
|
|
||||||
result := m.GetMatchingResourcesByCurrentId(
|
|
||||||
resid.NewResId(cmap, "alice").GvknEquals)
|
|
||||||
if len(result) != 1 {
|
|
||||||
t.Fatalf("Expected single map entry but got %v", result)
|
|
||||||
}
|
|
||||||
result = m.GetMatchingResourcesByCurrentId(
|
|
||||||
resid.NewResId(cmap, "bob").GvknEquals)
|
|
||||||
if len(result) != 2 {
|
|
||||||
t.Fatalf("Expected two, got %v", result)
|
|
||||||
}
|
|
||||||
result = m.GetMatchingResourcesByCurrentId(
|
|
||||||
resid.NewResIdWithNamespace(cmap, "bob", "system").GvknEquals)
|
|
||||||
if len(result) != 2 {
|
|
||||||
t.Fatalf("Expected two but got %v", result)
|
|
||||||
}
|
|
||||||
result = m.GetMatchingResourcesByCurrentId(
|
|
||||||
resid.NewResIdWithNamespace(cmap, "bob", "happy").Equals)
|
|
||||||
if len(result) != 1 {
|
|
||||||
t.Fatalf("Expected single map entry but got %v", result)
|
|
||||||
}
|
|
||||||
result = m.GetMatchingResourcesByCurrentId(
|
|
||||||
resid.NewResId(cmap, "charlie").GvknEquals)
|
|
||||||
if len(result) != 1 {
|
|
||||||
t.Fatalf("Expected single map entry but got %v", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:goconst
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
matcher IdMatcher
|
|
||||||
count int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"match everything",
|
|
||||||
func(resid.ResId) bool { return true },
|
|
||||||
5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"match nothing",
|
|
||||||
func(resid.ResId) bool { return false },
|
|
||||||
0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name is alice",
|
|
||||||
func(x resid.ResId) bool { return x.Name == "alice" },
|
|
||||||
1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name is charlie",
|
|
||||||
func(x resid.ResId) bool { return x.Name == "charlie" },
|
|
||||||
2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name is bob",
|
|
||||||
func(x resid.ResId) bool { return x.Name == "bob" },
|
|
||||||
2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"happy namespace",
|
|
||||||
func(x resid.ResId) bool {
|
|
||||||
return x.Namespace == "happy"
|
|
||||||
},
|
|
||||||
3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"happy deployment",
|
|
||||||
func(x resid.ResId) bool {
|
|
||||||
return x.Namespace == "happy" &&
|
|
||||||
x.Gvk.Kind == "Deployment"
|
|
||||||
},
|
|
||||||
1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"happy ConfigMap",
|
|
||||||
func(x resid.ResId) bool {
|
|
||||||
return x.Namespace == "happy" &&
|
|
||||||
x.Gvk.Kind == "ConfigMap"
|
|
||||||
},
|
|
||||||
2,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tst := range tests {
|
|
||||||
result := m.GetMatchingResourcesByCurrentId(tst.matcher)
|
|
||||||
if len(result) != tst.count {
|
|
||||||
t.Fatalf("test '%s'; actual: %d, expected: %d",
|
|
||||||
tst.name, len(result), tst.count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
|
|
||||||
r1 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "alice",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r2 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "bob",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r3 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "bob",
|
|
||||||
"namespace": "happy",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r4 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "charlie",
|
|
||||||
"namespace": "happy",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r5 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "charlie",
|
|
||||||
"namespace": "happy",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r5.AddNamePrefix("little-")
|
|
||||||
r6 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "domino",
|
|
||||||
"namespace": "happy",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r6.AddNamePrefix("little-")
|
|
||||||
r7 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ClusterRoleBinding",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "meh",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
tests := map[string]struct {
|
|
||||||
filter *resource.Resource
|
|
||||||
expected ResMap
|
|
||||||
}{
|
|
||||||
"default namespace 1": {
|
|
||||||
filter: r2,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(r1).AddR(r2).AddR(r7).ResMap(),
|
|
||||||
},
|
|
||||||
"default namespace 2": {
|
|
||||||
filter: r1,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(r1).AddR(r2).AddR(r7).ResMap(),
|
|
||||||
},
|
|
||||||
"happy namespace no prefix": {
|
|
||||||
filter: r3,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
|
||||||
},
|
|
||||||
"happy namespace with prefix": {
|
|
||||||
filter: r5,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
|
||||||
},
|
|
||||||
"cluster level": {
|
|
||||||
filter: r7,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
m := resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap()
|
|
||||||
for name, test := range tests {
|
|
||||||
test := test
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
got := m.SubsetThatCouldBeReferencedByResource(test.filter)
|
|
||||||
err := test.expected.ErrorIfNotEqualLists(got)
|
|
||||||
if err != nil {
|
|
||||||
test.expected.Debug("expected")
|
|
||||||
got.Debug("actual")
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addPfxSfx(r *resource.Resource, prefixes []string, suffixes []string) {
|
|
||||||
for _, pfx := range prefixes {
|
|
||||||
r.AddNamePrefix(pfx)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, sfx := range suffixes {
|
|
||||||
r.AddNameSuffix(sfx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSubsetThatCouldBeReferencedByResourceMultiLevel(t *testing.T) {
|
|
||||||
// Simulates ConfigMap and Deployment defined at level 1
|
|
||||||
// No prefix nor suffix added at that level
|
|
||||||
cm1 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(cm1, []string{""}, []string{""})
|
|
||||||
dep1 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(dep1, []string{""}, []string{""})
|
|
||||||
|
|
||||||
// Simulates ConfigMap and Deployment defined at level 1
|
|
||||||
// and prefix added in level 2 of kustomization
|
|
||||||
cm2p := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level2p",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(cm2p, []string{"", "level2p-"}, []string{"", ""})
|
|
||||||
|
|
||||||
dep2p := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level2p",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(dep2p, []string{"", "level2p-"}, []string{"", ""})
|
|
||||||
|
|
||||||
// Simulates ConfigMap and Deployment defined at level 1
|
|
||||||
// and suffix added in level 2 of kustomization
|
|
||||||
cm2s := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level2s",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(cm2s, []string{"", ""}, []string{"", "-level2s"})
|
|
||||||
|
|
||||||
dep2s := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level2s",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(dep2s, []string{"", ""}, []string{"", "-level2s"})
|
|
||||||
|
|
||||||
// Simulates ConfigMap and Deployment defined at level 1,
|
|
||||||
// prefix added in levels 2 and 3 of kustomization.
|
|
||||||
cm3e := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level3e",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(cm3e, []string{"", "level2p-", "level3e-"}, []string{"", "", ""})
|
|
||||||
|
|
||||||
dep3e := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level3e",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(dep3e, []string{"", "level2p-", "level3e-"}, []string{"", "", ""})
|
|
||||||
|
|
||||||
// Simulates Deployment defined at level 1, ConfigMap defined at level 2,
|
|
||||||
// prefix added in levels 2 and 3 of kustomization.
|
|
||||||
// This reproduce issue 1440.
|
|
||||||
cm3i := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level3i",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(cm3i, []string{"level2p-", "level3i-"}, []string{"", ""})
|
|
||||||
|
|
||||||
dep3i := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "level3i",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
addPfxSfx(dep3i, []string{"", "level2p-", "level3i-"}, []string{"", "", ""})
|
|
||||||
|
|
||||||
tests := map[string]struct {
|
|
||||||
filter *resource.Resource
|
|
||||||
expected ResMap
|
|
||||||
}{
|
|
||||||
"level1": {
|
|
||||||
filter: dep1,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(cm1).AddR(dep1).ResMap(),
|
|
||||||
},
|
|
||||||
"level2p": {
|
|
||||||
filter: dep2p,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(cm2p).AddR(dep2p).ResMap(),
|
|
||||||
},
|
|
||||||
"level2s": {
|
|
||||||
filter: dep2s,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(cm2s).AddR(dep2s).ResMap(),
|
|
||||||
},
|
|
||||||
"level3p": {
|
|
||||||
filter: dep3e,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(cm3e).AddR(dep3e).ResMap(),
|
|
||||||
},
|
|
||||||
"level3i": {
|
|
||||||
filter: dep3i,
|
|
||||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(cm3i).AddR(dep3i).ResMap(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
m := resmaptest_test.NewRmBuilder(t, rf).
|
|
||||||
AddR(cm1).AddR(dep1).AddR(cm2s).AddR(dep2s).AddR(cm2p).AddR(dep2p).AddR(cm3e).AddR(dep3e).AddR(cm3i).AddR(dep3i).ResMap()
|
|
||||||
for name, test := range tests {
|
|
||||||
test := test
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
got := m.SubsetThatCouldBeReferencedByResource(test.filter)
|
|
||||||
err := test.expected.ErrorIfNotEqualLists(got)
|
|
||||||
if err != nil {
|
|
||||||
test.expected.Debug("expected")
|
|
||||||
got.Debug("actual")
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeepCopy(t *testing.T) {
|
|
||||||
rm1 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm1",
|
|
||||||
},
|
|
||||||
}).Add(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm2",
|
|
||||||
},
|
|
||||||
}).ResMap()
|
|
||||||
|
|
||||||
rm2 := rm1.DeepCopy()
|
|
||||||
|
|
||||||
if &rm1 == &rm2 {
|
|
||||||
t.Fatal("DeepCopy returned a reference to itself instead of a copy")
|
|
||||||
}
|
|
||||||
err := rm1.ErrorIfNotEqualLists(rm1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorIfNotEqualSets(t *testing.T) {
|
|
||||||
r1 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r2 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm2",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r3 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm2",
|
|
||||||
"namespace": "system",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualSets(m1); err != nil {
|
|
||||||
t.Fatalf("object should equal itself %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualSets(m2); err == nil {
|
|
||||||
t.Fatalf("%v should not equal %v %v", m1, m2, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m3 := resmaptest_test.NewRmBuilder(t, rf).AddR(r2).ResMap()
|
|
||||||
if err := m2.ErrorIfNotEqualSets(m3); err == nil {
|
|
||||||
t.Fatalf("%v should not equal %v %v", m2, m3, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m3 = resmaptest_test.NewRmBuilder(t, rf).Add(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm1",
|
|
||||||
}}).ResMap()
|
|
||||||
if err := m2.ErrorIfNotEqualSets(m3); err != nil {
|
|
||||||
t.Fatalf("%v should equal %v %v", m2, m3, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
|
||||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
|
||||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m4 = m1.ShallowCopy()
|
|
||||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
|
||||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
m4 = m1.DeepCopy()
|
|
||||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
|
||||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorIfNotEqualLists(t *testing.T) {
|
|
||||||
r1 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r2 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm2",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
r3 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm2",
|
|
||||||
"namespace": "system",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualLists(m1); err != nil {
|
|
||||||
t.Fatalf("object should equal itself %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualLists(m2); err == nil {
|
|
||||||
t.Fatalf("%v should not equal %v %v", m1, m2, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m3 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cm1",
|
|
||||||
}}).ResMap()
|
|
||||||
if err := m2.ErrorIfNotEqualLists(m3); err != nil {
|
|
||||||
t.Fatalf("%v should equal %v %v", m2, m3, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
|
||||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap()
|
|
||||||
if err := m1.ErrorIfNotEqualLists(m4); err == nil {
|
|
||||||
t.Fatalf("expected inequality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m4 = m1.ShallowCopy()
|
|
||||||
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
|
||||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
m4 = m1.DeepCopy()
|
|
||||||
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
|
||||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAppendAll(t *testing.T) {
|
|
||||||
r1 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "foo-deploy1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
input1 := rmF.FromResource(r1)
|
|
||||||
r2 := rf.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "StatefulSet",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "bar-stateful",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
input2 := rmF.FromResource(r2)
|
|
||||||
|
|
||||||
expected := New()
|
|
||||||
if err := expected.Append(r1); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if err := expected.Append(r2); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := input1.AppendAll(input2); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if err := expected.ErrorIfNotEqualLists(input1); err != nil {
|
|
||||||
input1.Debug("1")
|
|
||||||
expected.Debug("ex")
|
|
||||||
t.Fatalf("%#v doesn't equal expected %#v", input1, expected)
|
|
||||||
}
|
|
||||||
if err := input1.AppendAll(nil); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if err := expected.ErrorIfNotEqualLists(input1); err != nil {
|
|
||||||
t.Fatalf("%#v doesn't equal expected %#v", input1, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeMap1() ResMap {
|
|
||||||
return rmF.FromResource(rf.FromMapAndOption(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cmap",
|
|
||||||
},
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"a": "x",
|
|
||||||
"b": "y",
|
|
||||||
},
|
|
||||||
}, &types.GeneratorArgs{
|
|
||||||
Behavior: "create",
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeMap2(b types.GenerationBehavior) ResMap {
|
|
||||||
return rmF.FromResource(rf.FromMapAndOption(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": "cmap",
|
|
||||||
},
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"a": "u",
|
|
||||||
"b": "v",
|
|
||||||
"c": "w",
|
|
||||||
},
|
|
||||||
}, &types.GeneratorArgs{
|
|
||||||
Behavior: b.String(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAbsorbAll(t *testing.T) {
|
|
||||||
expected := rmF.FromResource(rf.FromMapAndOption(
|
|
||||||
map[string]interface{}{
|
|
||||||
"apiVersion": "apps/v1",
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"annotations": map[string]interface{}{},
|
|
||||||
"labels": map[string]interface{}{},
|
|
||||||
"name": "cmap",
|
|
||||||
},
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"a": "u",
|
|
||||||
"b": "v",
|
|
||||||
"c": "w",
|
|
||||||
},
|
|
||||||
}, &types.GeneratorArgs{
|
|
||||||
Behavior: "create",
|
|
||||||
}))
|
|
||||||
w := makeMap1()
|
|
||||||
if err := w.AbsorbAll(makeMap2(types.BehaviorMerge)); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if err := expected.ErrorIfNotEqualLists(w); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
w = makeMap1()
|
|
||||||
if err := w.AbsorbAll(nil); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if err := w.ErrorIfNotEqualLists(makeMap1()); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
w = makeMap1()
|
|
||||||
w2 := makeMap2(types.BehaviorReplace)
|
|
||||||
if err := w.AbsorbAll(w2); err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if err := w2.ErrorIfNotEqualLists(w); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
w = makeMap1()
|
|
||||||
w2 = makeMap2(types.BehaviorUnspecified)
|
|
||||||
err := w.AbsorbAll(w2)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error with unspecified behavior")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
581
api/resmap/reswrangler.go
Normal file
581
api/resmap/reswrangler.go
Normal file
@@ -0,0 +1,581 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package resmap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// resWrangler implements ResMap.
|
||||||
|
type resWrangler struct {
|
||||||
|
// Resource list maintained in load (append) order.
|
||||||
|
// This is important for transformers, which must
|
||||||
|
// be performed in a specific order, and for users
|
||||||
|
// who for whatever reasons wish the order they
|
||||||
|
// specify in kustomizations to be maintained and
|
||||||
|
// available as an option for final YAML rendering.
|
||||||
|
rList []*resource.Resource
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOne() *resWrangler {
|
||||||
|
result := &resWrangler{}
|
||||||
|
result.Clear()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear implements ResMap.
|
||||||
|
func (m *resWrangler) Clear() {
|
||||||
|
m.rList = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size implements ResMap.
|
||||||
|
func (m *resWrangler) Size() int {
|
||||||
|
return len(m.rList)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resWrangler) indexOfResource(other *resource.Resource) int {
|
||||||
|
for i, r := range m.rList {
|
||||||
|
if r == other {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources implements ResMap.
|
||||||
|
func (m *resWrangler) Resources() []*resource.Resource {
|
||||||
|
tmp := make([]*resource.Resource, len(m.rList))
|
||||||
|
copy(tmp, m.rList)
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append implements ResMap.
|
||||||
|
func (m *resWrangler) Append(res *resource.Resource) error {
|
||||||
|
id := res.CurId()
|
||||||
|
if r := m.GetMatchingResourcesByCurrentId(id.Equals); len(r) > 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"may not add resource with an already registered id: %s", id)
|
||||||
|
}
|
||||||
|
m.rList = append(m.rList, res)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove implements ResMap.
|
||||||
|
func (m *resWrangler) Remove(adios resid.ResId) error {
|
||||||
|
tmp := newOne()
|
||||||
|
for _, r := range m.rList {
|
||||||
|
if r.CurId() != adios {
|
||||||
|
tmp.Append(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tmp.Size() != m.Size()-1 {
|
||||||
|
return fmt.Errorf("id %s not found in removal", adios)
|
||||||
|
}
|
||||||
|
m.rList = tmp.rList
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace implements ResMap.
|
||||||
|
func (m *resWrangler) Replace(res *resource.Resource) (int, error) {
|
||||||
|
id := res.CurId()
|
||||||
|
i, err := m.GetIndexOfCurrentId(id)
|
||||||
|
if err != nil {
|
||||||
|
return -1, errors.Wrap(err, "in Replace")
|
||||||
|
}
|
||||||
|
if i < 0 {
|
||||||
|
return -1, fmt.Errorf("cannot find resource with id %s to replace", id)
|
||||||
|
}
|
||||||
|
m.rList[i] = res
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllIds implements ResMap.
|
||||||
|
func (m *resWrangler) AllIds() (ids []resid.ResId) {
|
||||||
|
ids = make([]resid.ResId, m.Size())
|
||||||
|
for i, r := range m.rList {
|
||||||
|
ids[i] = r.CurId()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug implements ResMap.
|
||||||
|
func (m *resWrangler) Debug(title string) {
|
||||||
|
fmt.Println("--------------------------- " + title)
|
||||||
|
firstObj := true
|
||||||
|
for i, r := range m.rList {
|
||||||
|
if firstObj {
|
||||||
|
firstObj = false
|
||||||
|
} else {
|
||||||
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
fmt.Printf("# %d %s\n", i, r.OrgId())
|
||||||
|
blob, err := yaml.Marshal(r.Map())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(blob))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdMatcher func(resid.ResId) bool
|
||||||
|
|
||||||
|
// GetByIndex implements ResMap.
|
||||||
|
func (m *resWrangler) GetByIndex(i int) *resource.Resource {
|
||||||
|
if i < 0 || i >= m.Size() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m.rList[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIndexOfCurrentId implements ResMap.
|
||||||
|
func (m *resWrangler) GetIndexOfCurrentId(id resid.ResId) (int, error) {
|
||||||
|
count := 0
|
||||||
|
result := -1
|
||||||
|
for i, r := range m.rList {
|
||||||
|
if id.Equals(r.CurId()) {
|
||||||
|
count++
|
||||||
|
result = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if count > 1 {
|
||||||
|
return -1, fmt.Errorf("id matched %d resources", count)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdFromResource func(r *resource.Resource) resid.ResId
|
||||||
|
|
||||||
|
func GetOriginalId(r *resource.Resource) resid.ResId { return r.OrgId() }
|
||||||
|
func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() }
|
||||||
|
|
||||||
|
// GetMatchingResourcesByCurrentId implements ResMap.
|
||||||
|
func (m *resWrangler) GetMatchingResourcesByCurrentId(
|
||||||
|
matches IdMatcher) []*resource.Resource {
|
||||||
|
return m.filteredById(matches, GetCurrentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMatchingResourcesByOriginalId implements ResMap.
|
||||||
|
func (m *resWrangler) GetMatchingResourcesByOriginalId(
|
||||||
|
matches IdMatcher) []*resource.Resource {
|
||||||
|
return m.filteredById(matches, GetOriginalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resWrangler) filteredById(
|
||||||
|
matches IdMatcher, idGetter IdFromResource) []*resource.Resource {
|
||||||
|
var result []*resource.Resource
|
||||||
|
for _, r := range m.rList {
|
||||||
|
if matches(idGetter(r)) {
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByCurrentId implements ResMap.
|
||||||
|
func (m *resWrangler) GetByCurrentId(
|
||||||
|
id resid.ResId) (*resource.Resource, error) {
|
||||||
|
return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByOriginalId implements ResMap.
|
||||||
|
func (m *resWrangler) GetByOriginalId(
|
||||||
|
id resid.ResId) (*resource.Resource, error) {
|
||||||
|
return demandOneMatch(m.GetMatchingResourcesByOriginalId, id, "Original")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetById implements ResMap.
|
||||||
|
func (m *resWrangler) GetById(
|
||||||
|
id resid.ResId) (*resource.Resource, error) {
|
||||||
|
match, err1 := m.GetByOriginalId(id)
|
||||||
|
if err1 == nil {
|
||||||
|
return match, nil
|
||||||
|
}
|
||||||
|
match, err2 := m.GetByCurrentId(id)
|
||||||
|
if err2 == nil {
|
||||||
|
return match, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"%s; %s; failed to find unique target for patch %s",
|
||||||
|
err1.Error(), err2.Error(), id.GvknString())
|
||||||
|
}
|
||||||
|
|
||||||
|
type resFinder func(IdMatcher) []*resource.Resource
|
||||||
|
|
||||||
|
func demandOneMatch(
|
||||||
|
f resFinder, id resid.ResId, s string) (*resource.Resource, error) {
|
||||||
|
r := f(id.Equals)
|
||||||
|
if len(r) == 1 {
|
||||||
|
return r[0], nil
|
||||||
|
}
|
||||||
|
if len(r) > 1 {
|
||||||
|
return nil, fmt.Errorf("multiple matches for %sId %s", s, id)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no matches for %sId %s", s, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupedByCurrentNamespace implements ResMap.GroupByCurrentNamespace
|
||||||
|
func (m *resWrangler) GroupedByCurrentNamespace() map[string][]*resource.Resource {
|
||||||
|
items := m.groupedByCurrentNamespace()
|
||||||
|
delete(items, resid.TotallyNotANamespace)
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonNamespaceable implements ResMap.NonNamespaceable
|
||||||
|
func (m *resWrangler) NonNamespaceable() []*resource.Resource {
|
||||||
|
return m.groupedByCurrentNamespace()[resid.TotallyNotANamespace]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resWrangler) groupedByCurrentNamespace() map[string][]*resource.Resource {
|
||||||
|
byNamespace := make(map[string][]*resource.Resource)
|
||||||
|
for _, res := range m.rList {
|
||||||
|
namespace := res.CurId().EffectiveNamespace()
|
||||||
|
if _, found := byNamespace[namespace]; !found {
|
||||||
|
byNamespace[namespace] = []*resource.Resource{}
|
||||||
|
}
|
||||||
|
byNamespace[namespace] = append(byNamespace[namespace], res)
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
func (m *resWrangler) AsYaml() ([]byte, error) {
|
||||||
|
firstObj := true
|
||||||
|
var b []byte
|
||||||
|
buf := bytes.NewBuffer(b)
|
||||||
|
for _, res := range m.Resources() {
|
||||||
|
out, err := yaml.Marshal(res.Map())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if firstObj {
|
||||||
|
firstObj = false
|
||||||
|
} else {
|
||||||
|
if _, err = buf.WriteString("---\n"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, err = buf.Write(out); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorIfNotEqualSets implements ResMap.
|
||||||
|
func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error {
|
||||||
|
m2, ok := other.(*resWrangler)
|
||||||
|
if !ok {
|
||||||
|
panic("bad cast")
|
||||||
|
}
|
||||||
|
if m.Size() != m2.Size() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"lists have different number of entries: %#v doesn't equal %#v",
|
||||||
|
m.rList, m2.rList)
|
||||||
|
}
|
||||||
|
seen := make(map[int]bool)
|
||||||
|
for _, r1 := range m.rList {
|
||||||
|
id := r1.CurId()
|
||||||
|
others := m2.GetMatchingResourcesByCurrentId(id.Equals)
|
||||||
|
if len(others) == 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"id in self missing from other; id: %s", id)
|
||||||
|
}
|
||||||
|
if len(others) > 1 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"id in self matches %d in other; id: %s", len(others), id)
|
||||||
|
}
|
||||||
|
r2 := others[0]
|
||||||
|
if !r1.KunstructEqual(r2) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"kunstruct not equal: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
|
||||||
|
r1, r2, r1, r2)
|
||||||
|
}
|
||||||
|
seen[m2.indexOfResource(r2)] = true
|
||||||
|
}
|
||||||
|
if len(seen) != m.Size() {
|
||||||
|
return fmt.Errorf("counting problem %d != %d", len(seen), m.Size())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorIfNotEqualList implements ResMap.
|
||||||
|
func (m *resWrangler) ErrorIfNotEqualLists(other ResMap) error {
|
||||||
|
m2, ok := other.(*resWrangler)
|
||||||
|
if !ok {
|
||||||
|
panic("bad cast")
|
||||||
|
}
|
||||||
|
if m.Size() != m2.Size() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"lists have different number of entries: %#v doesn't equal %#v",
|
||||||
|
m.rList, m2.rList)
|
||||||
|
}
|
||||||
|
for i, r1 := range m.rList {
|
||||||
|
r2 := m2.rList[i]
|
||||||
|
if !r1.Equals(r2) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"Item i=%d differs:\n n1 = %s\n n2 = %s\n o1 = %s\n o2 = %s\n",
|
||||||
|
i, r1.OrgId(), r2.OrgId(), r1, r2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type resCopier func(r *resource.Resource) *resource.Resource
|
||||||
|
|
||||||
|
// ShallowCopy implements ResMap.
|
||||||
|
func (m *resWrangler) ShallowCopy() ResMap {
|
||||||
|
return m.makeCopy(
|
||||||
|
func(r *resource.Resource) *resource.Resource {
|
||||||
|
return r
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy implements ResMap.
|
||||||
|
func (m *resWrangler) DeepCopy() ResMap {
|
||||||
|
return m.makeCopy(
|
||||||
|
func(r *resource.Resource) *resource.Resource {
|
||||||
|
return r.DeepCopy()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeCopy copies the ResMap.
|
||||||
|
func (m *resWrangler) makeCopy(copier resCopier) ResMap {
|
||||||
|
result := &resWrangler{}
|
||||||
|
result.rList = make([]*resource.Resource, m.Size())
|
||||||
|
for i, r := range m.rList {
|
||||||
|
result.rList[i] = copier(r)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubsetThatCouldBeReferencedByResource implements ResMap.
|
||||||
|
func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
|
||||||
|
inputRes *resource.Resource) ResMap {
|
||||||
|
result := newOne()
|
||||||
|
inputId := inputRes.CurId()
|
||||||
|
isInputIdNamespaceable := inputId.IsNamespaceableKind()
|
||||||
|
subjectNamespaces := getNamespacesForRoleBinding(inputRes)
|
||||||
|
for _, r := range m.Resources() {
|
||||||
|
// Need to match more accuratly both at the time of selection and transformation.
|
||||||
|
// OutmostPrefixSuffixEquals is not accurate enough since it is only using
|
||||||
|
// the outer most suffix and the last prefix. Use PrefixedSuffixesEquals instead.
|
||||||
|
resId := r.CurId()
|
||||||
|
if !isInputIdNamespaceable || !resId.IsNamespaceableKind() || resId.IsNsEquals(inputId) ||
|
||||||
|
isRoleBindingNamespace(&subjectNamespaces, r.GetNamespace()) {
|
||||||
|
result.append(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// isRoleBindingNamespace returns true is the namespace `ns` is in role binding
|
||||||
|
// namespaces `m`
|
||||||
|
func isRoleBindingNamespace(m *map[string]bool, ns string) bool {
|
||||||
|
return (*m)[ns]
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNamespacesForRoleBinding returns referenced ServiceAccount namespaces if the inputRes is
|
||||||
|
// a RoleBinding
|
||||||
|
func getNamespacesForRoleBinding(inputRes *resource.Resource) map[string]bool {
|
||||||
|
res := make(map[string]bool)
|
||||||
|
if inputRes.GetKind() != "RoleBinding" {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
subjects, err := inputRes.GetSlice("subjects")
|
||||||
|
if err != nil || subjects == nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range subjects {
|
||||||
|
subject := s.(map[string]interface{})
|
||||||
|
if subject["namespace"] == nil || subject["kind"] == nil ||
|
||||||
|
subject["kind"].(string) != "ServiceAccount" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res[subject["namespace"].(string)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resWrangler) append(res *resource.Resource) {
|
||||||
|
m.rList = append(m.rList, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppendAll implements ResMap.
|
||||||
|
func (m *resWrangler) AppendAll(other ResMap) error {
|
||||||
|
if other == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, res := range other.Resources() {
|
||||||
|
if err := m.Append(res); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsorbAll implements ResMap.
|
||||||
|
func (m *resWrangler) AbsorbAll(other ResMap) error {
|
||||||
|
if other == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, r := range other.Resources() {
|
||||||
|
err := m.appendReplaceOrMerge(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *resWrangler) appendReplaceOrMerge(
|
||||||
|
res *resource.Resource) error {
|
||||||
|
id := res.CurId()
|
||||||
|
matches := m.GetMatchingResourcesByOriginalId(id.Equals)
|
||||||
|
if len(matches) == 0 {
|
||||||
|
matches = m.GetMatchingResourcesByCurrentId(id.Equals)
|
||||||
|
}
|
||||||
|
switch len(matches) {
|
||||||
|
case 0:
|
||||||
|
switch res.Behavior() {
|
||||||
|
case types.BehaviorMerge, types.BehaviorReplace:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"id %#v does not exist; cannot merge or replace", id)
|
||||||
|
default:
|
||||||
|
// presumably types.BehaviorCreate
|
||||||
|
err := m.Append(res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
old := matches[0]
|
||||||
|
if old == nil {
|
||||||
|
return fmt.Errorf("id lookup failure")
|
||||||
|
}
|
||||||
|
index := m.indexOfResource(old)
|
||||||
|
if index < 0 {
|
||||||
|
return fmt.Errorf("indexing problem")
|
||||||
|
}
|
||||||
|
switch res.Behavior() {
|
||||||
|
case types.BehaviorReplace:
|
||||||
|
res.Replace(old)
|
||||||
|
case types.BehaviorMerge:
|
||||||
|
res.Merge(old)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"id %#v exists; behavior must be merge or replace", id)
|
||||||
|
}
|
||||||
|
i, err := m.Replace(res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if i != index {
|
||||||
|
return fmt.Errorf("unexpected index in replacement")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"found multiple objects %v that could accept merge of %v",
|
||||||
|
matches, id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func anchorRegex(pattern string) string {
|
||||||
|
if pattern == "" {
|
||||||
|
return pattern
|
||||||
|
}
|
||||||
|
return "^" + pattern + "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select returns a list of resources that
|
||||||
|
// are selected by a Selector
|
||||||
|
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
|
||||||
|
ns := regexp.MustCompile(anchorRegex(s.Namespace))
|
||||||
|
nm := regexp.MustCompile(anchorRegex(s.Name))
|
||||||
|
var result []*resource.Resource
|
||||||
|
for _, r := range m.Resources() {
|
||||||
|
curId := r.CurId()
|
||||||
|
orgId := r.OrgId()
|
||||||
|
|
||||||
|
// matches the namespace when namespace is not empty in the selector
|
||||||
|
// It first tries to match with the original namespace
|
||||||
|
// then matches with the current namespace
|
||||||
|
if r.GetNamespace() != "" {
|
||||||
|
matched := ns.MatchString(orgId.EffectiveNamespace())
|
||||||
|
if !matched {
|
||||||
|
matched = ns.MatchString(curId.EffectiveNamespace())
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches the name when name is not empty in the selector
|
||||||
|
// It first tries to match with the original name
|
||||||
|
// then matches with the current name
|
||||||
|
if r.GetName() != "" {
|
||||||
|
matched := nm.MatchString(orgId.Name)
|
||||||
|
if !matched {
|
||||||
|
matched = nm.MatchString(curId.Name)
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches the GVK
|
||||||
|
if !r.GetGvk().IsSelected(&s.Gvk) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches the label selector
|
||||||
|
matched, err := r.MatchesLabelSelector(s.LabelSelector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches the annotation selector
|
||||||
|
matched, err = r.MatchesAnnotationSelector(s.AnnotationSelector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, r)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
734
api/resmap/reswrangler_test.go
Normal file
734
api/resmap/reswrangler_test.go
Normal file
@@ -0,0 +1,734 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package resmap_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
. "sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rf = resource.NewFactory(
|
||||||
|
kunstruct.NewKunstructuredFactoryImpl())
|
||||||
|
var rmF = NewFactory(rf, nil)
|
||||||
|
|
||||||
|
func doAppend(t *testing.T, w ResMap, r *resource.Resource) {
|
||||||
|
err := w.Append(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("append error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func doRemove(t *testing.T, w ResMap, id resid.ResId) {
|
||||||
|
err := w.Remove(id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("remove error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a resource with a predictable name.
|
||||||
|
func makeCm(i int) *resource.Resource {
|
||||||
|
return rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": fmt.Sprintf("cm%03d", i),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain the class invariant that no two
|
||||||
|
// resources can have the same CurId().
|
||||||
|
func TestAppendRejectsDuplicateResId(t *testing.T) {
|
||||||
|
w := New()
|
||||||
|
if err := w.Append(makeCm(1)); err != nil {
|
||||||
|
t.Fatalf("append error: %v", err)
|
||||||
|
}
|
||||||
|
err := w.Append(makeCm(1))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected append error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(),
|
||||||
|
"may not add resource with an already registered id") {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendRemove(t *testing.T) {
|
||||||
|
w1 := New()
|
||||||
|
doAppend(t, w1, makeCm(1))
|
||||||
|
doAppend(t, w1, makeCm(2))
|
||||||
|
doAppend(t, w1, makeCm(3))
|
||||||
|
doAppend(t, w1, makeCm(4))
|
||||||
|
doAppend(t, w1, makeCm(5))
|
||||||
|
doAppend(t, w1, makeCm(6))
|
||||||
|
doAppend(t, w1, makeCm(7))
|
||||||
|
doRemove(t, w1, makeCm(1).OrgId())
|
||||||
|
doRemove(t, w1, makeCm(3).OrgId())
|
||||||
|
doRemove(t, w1, makeCm(5).OrgId())
|
||||||
|
doRemove(t, w1, makeCm(7).OrgId())
|
||||||
|
|
||||||
|
w2 := New()
|
||||||
|
doAppend(t, w2, makeCm(2))
|
||||||
|
doAppend(t, w2, makeCm(4))
|
||||||
|
doAppend(t, w2, makeCm(6))
|
||||||
|
if !reflect.DeepEqual(w1, w1) {
|
||||||
|
w1.Debug("w1")
|
||||||
|
w2.Debug("w2")
|
||||||
|
t.Fatalf("mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := w2.Append(makeCm(6))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemove(t *testing.T) {
|
||||||
|
w := New()
|
||||||
|
r := makeCm(1)
|
||||||
|
err := w.Remove(r.OrgId())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
err = w.Append(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
err = w.Remove(r.OrgId())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
err = w.Remove(r.OrgId())
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplace(t *testing.T) {
|
||||||
|
cm5 := makeCm(5)
|
||||||
|
cm700 := makeCm(700)
|
||||||
|
otherCm5 := makeCm(5)
|
||||||
|
|
||||||
|
w := New()
|
||||||
|
doAppend(t, w, makeCm(1))
|
||||||
|
doAppend(t, w, makeCm(2))
|
||||||
|
doAppend(t, w, makeCm(3))
|
||||||
|
doAppend(t, w, makeCm(4))
|
||||||
|
doAppend(t, w, cm5)
|
||||||
|
doAppend(t, w, makeCm(6))
|
||||||
|
doAppend(t, w, makeCm(7))
|
||||||
|
|
||||||
|
oldSize := w.Size()
|
||||||
|
_, err := w.Replace(otherCm5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if w.Size() != oldSize {
|
||||||
|
t.Fatalf("unexpected size %d", w.Size())
|
||||||
|
}
|
||||||
|
if r, err := w.GetByCurrentId(cm5.OrgId()); err != nil || r != otherCm5 {
|
||||||
|
t.Fatalf("unexpected result r=%s, err=%v", r.CurId(), err)
|
||||||
|
}
|
||||||
|
if err := w.Append(cm5); err == nil {
|
||||||
|
t.Fatalf("expected id already there error")
|
||||||
|
}
|
||||||
|
if err := w.Remove(cm5.OrgId()); err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if err := w.Append(cm700); err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if err := w.Append(cm5); err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeAsYaml(t *testing.T) {
|
||||||
|
encoded := []byte(`apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm2
|
||||||
|
`)
|
||||||
|
input := resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
},
|
||||||
|
}).Add(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm2",
|
||||||
|
},
|
||||||
|
}).ResMap()
|
||||||
|
out, err := input.AsYaml()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, encoded) {
|
||||||
|
t.Fatalf("%s doesn't match expected %s", out, encoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMatchingResourcesByCurrentId(t *testing.T) {
|
||||||
|
r1 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "alice",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r2 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "bob",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r3 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "bob",
|
||||||
|
"namespace": "happy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r4 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "charlie",
|
||||||
|
"namespace": "happy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r5 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "charlie",
|
||||||
|
"namespace": "happy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
m := resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).ResMap()
|
||||||
|
|
||||||
|
result := m.GetMatchingResourcesByCurrentId(
|
||||||
|
resid.NewResId(cmap, "alice").GvknEquals)
|
||||||
|
if len(result) != 1 {
|
||||||
|
t.Fatalf("Expected single map entry but got %v", result)
|
||||||
|
}
|
||||||
|
result = m.GetMatchingResourcesByCurrentId(
|
||||||
|
resid.NewResId(cmap, "bob").GvknEquals)
|
||||||
|
if len(result) != 2 {
|
||||||
|
t.Fatalf("Expected two, got %v", result)
|
||||||
|
}
|
||||||
|
result = m.GetMatchingResourcesByCurrentId(
|
||||||
|
resid.NewResIdWithNamespace(cmap, "bob", "system").GvknEquals)
|
||||||
|
if len(result) != 2 {
|
||||||
|
t.Fatalf("Expected two but got %v", result)
|
||||||
|
}
|
||||||
|
result = m.GetMatchingResourcesByCurrentId(
|
||||||
|
resid.NewResIdWithNamespace(cmap, "bob", "happy").Equals)
|
||||||
|
if len(result) != 1 {
|
||||||
|
t.Fatalf("Expected single map entry but got %v", result)
|
||||||
|
}
|
||||||
|
result = m.GetMatchingResourcesByCurrentId(
|
||||||
|
resid.NewResId(cmap, "charlie").GvknEquals)
|
||||||
|
if len(result) != 1 {
|
||||||
|
t.Fatalf("Expected single map entry but got %v", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:goconst
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
matcher IdMatcher
|
||||||
|
count int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"match everything",
|
||||||
|
func(resid.ResId) bool { return true },
|
||||||
|
5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match nothing",
|
||||||
|
func(resid.ResId) bool { return false },
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name is alice",
|
||||||
|
func(x resid.ResId) bool { return x.Name == "alice" },
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name is charlie",
|
||||||
|
func(x resid.ResId) bool { return x.Name == "charlie" },
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name is bob",
|
||||||
|
func(x resid.ResId) bool { return x.Name == "bob" },
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"happy namespace",
|
||||||
|
func(x resid.ResId) bool {
|
||||||
|
return x.Namespace == "happy"
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"happy deployment",
|
||||||
|
func(x resid.ResId) bool {
|
||||||
|
return x.Namespace == "happy" &&
|
||||||
|
x.Gvk.Kind == "Deployment"
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"happy ConfigMap",
|
||||||
|
func(x resid.ResId) bool {
|
||||||
|
return x.Namespace == "happy" &&
|
||||||
|
x.Gvk.Kind == "ConfigMap"
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tst := range tests {
|
||||||
|
result := m.GetMatchingResourcesByCurrentId(tst.matcher)
|
||||||
|
if len(result) != tst.count {
|
||||||
|
t.Fatalf("test '%s'; actual: %d, expected: %d",
|
||||||
|
tst.name, len(result), tst.count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
|
||||||
|
r1 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "alice",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r2 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "bob",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r3 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "bob",
|
||||||
|
"namespace": "happy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r4 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "charlie",
|
||||||
|
"namespace": "happy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r5 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "charlie",
|
||||||
|
"namespace": "happy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r5.AddNamePrefix("little-")
|
||||||
|
r6 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "domino",
|
||||||
|
"namespace": "happy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r6.AddNamePrefix("little-")
|
||||||
|
r7 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ClusterRoleBinding",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "meh",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
filter *resource.Resource
|
||||||
|
expected ResMap
|
||||||
|
}{
|
||||||
|
"default namespace 1": {
|
||||||
|
filter: r2,
|
||||||
|
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
AddR(r1).AddR(r2).AddR(r7).ResMap(),
|
||||||
|
},
|
||||||
|
"default namespace 2": {
|
||||||
|
filter: r1,
|
||||||
|
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
AddR(r1).AddR(r2).AddR(r7).ResMap(),
|
||||||
|
},
|
||||||
|
"happy namespace no prefix": {
|
||||||
|
filter: r3,
|
||||||
|
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
||||||
|
},
|
||||||
|
"happy namespace with prefix": {
|
||||||
|
filter: r5,
|
||||||
|
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
||||||
|
},
|
||||||
|
"cluster level": {
|
||||||
|
filter: r7,
|
||||||
|
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
m := resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap()
|
||||||
|
for name, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
got := m.SubsetThatCouldBeReferencedByResource(test.filter)
|
||||||
|
err := test.expected.ErrorIfNotEqualLists(got)
|
||||||
|
if err != nil {
|
||||||
|
test.expected.Debug("expected")
|
||||||
|
got.Debug("actual")
|
||||||
|
t.Fatalf("Expected match")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeepCopy(t *testing.T) {
|
||||||
|
rm1 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
},
|
||||||
|
}).Add(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm2",
|
||||||
|
},
|
||||||
|
}).ResMap()
|
||||||
|
|
||||||
|
rm2 := rm1.DeepCopy()
|
||||||
|
|
||||||
|
if &rm1 == &rm2 {
|
||||||
|
t.Fatal("DeepCopy returned a reference to itself instead of a copy")
|
||||||
|
}
|
||||||
|
err := rm1.ErrorIfNotEqualLists(rm1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorIfNotEqualSets(t *testing.T) {
|
||||||
|
r1 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r2 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm2",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r3 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm2",
|
||||||
|
"namespace": "system",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualSets(m1); err != nil {
|
||||||
|
t.Fatalf("object should equal itself %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualSets(m2); err == nil {
|
||||||
|
t.Fatalf("%v should not equal %v %v", m1, m2, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m3 := resmaptest_test.NewRmBuilder(t, rf).AddR(r2).ResMap()
|
||||||
|
if err := m2.ErrorIfNotEqualSets(m3); err == nil {
|
||||||
|
t.Fatalf("%v should not equal %v %v", m2, m3, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m3 = resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
}}).ResMap()
|
||||||
|
if err := m2.ErrorIfNotEqualSets(m3); err != nil {
|
||||||
|
t.Fatalf("%v should equal %v %v", m2, m3, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||||
|
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||||
|
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m4 = m1.ShallowCopy()
|
||||||
|
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||||
|
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
m4 = m1.DeepCopy()
|
||||||
|
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||||
|
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorIfNotEqualLists(t *testing.T) {
|
||||||
|
r1 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r2 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm2",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
r3 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm2",
|
||||||
|
"namespace": "system",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualLists(m1); err != nil {
|
||||||
|
t.Fatalf("object should equal itself %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualLists(m2); err == nil {
|
||||||
|
t.Fatalf("%v should not equal %v %v", m1, m2, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m3 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
}}).ResMap()
|
||||||
|
if err := m2.ErrorIfNotEqualLists(m3); err != nil {
|
||||||
|
t.Fatalf("%v should equal %v %v", m2, m3, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
||||||
|
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap()
|
||||||
|
if err := m1.ErrorIfNotEqualLists(m4); err == nil {
|
||||||
|
t.Fatalf("expected inequality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m4 = m1.ShallowCopy()
|
||||||
|
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
||||||
|
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
m4 = m1.DeepCopy()
|
||||||
|
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
||||||
|
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendAll(t *testing.T) {
|
||||||
|
r1 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-deploy1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
input1 := rmF.FromResource(r1)
|
||||||
|
r2 := rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "StatefulSet",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "bar-stateful",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
input2 := rmF.FromResource(r2)
|
||||||
|
|
||||||
|
expected := New()
|
||||||
|
if err := expected.Append(r1); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := expected.Append(r2); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := input1.AppendAll(input2); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := expected.ErrorIfNotEqualLists(input1); err != nil {
|
||||||
|
input1.Debug("1")
|
||||||
|
expected.Debug("ex")
|
||||||
|
t.Fatalf("%#v doesn't equal expected %#v", input1, expected)
|
||||||
|
}
|
||||||
|
if err := input1.AppendAll(nil); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := expected.ErrorIfNotEqualLists(input1); err != nil {
|
||||||
|
t.Fatalf("%#v doesn't equal expected %#v", input1, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeMap1() ResMap {
|
||||||
|
return rmF.FromResource(rf.FromMapAndOption(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cmap",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"a": "x",
|
||||||
|
"b": "y",
|
||||||
|
},
|
||||||
|
}, &types.GeneratorArgs{
|
||||||
|
Behavior: "create",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeMap2(b types.GenerationBehavior) ResMap {
|
||||||
|
return rmF.FromResource(rf.FromMapAndOption(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cmap",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"a": "u",
|
||||||
|
"b": "v",
|
||||||
|
"c": "w",
|
||||||
|
},
|
||||||
|
}, &types.GeneratorArgs{
|
||||||
|
Behavior: b.String(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAbsorbAll(t *testing.T) {
|
||||||
|
expected := rmF.FromResource(rf.FromMapAndOption(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"annotations": map[string]interface{}{},
|
||||||
|
"labels": map[string]interface{}{},
|
||||||
|
"name": "cmap",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"a": "u",
|
||||||
|
"b": "v",
|
||||||
|
"c": "w",
|
||||||
|
},
|
||||||
|
}, &types.GeneratorArgs{
|
||||||
|
Behavior: "create",
|
||||||
|
}))
|
||||||
|
w := makeMap1()
|
||||||
|
if err := w.AbsorbAll(makeMap2(types.BehaviorMerge)); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := expected.ErrorIfNotEqualLists(w); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
w = makeMap1()
|
||||||
|
if err := w.AbsorbAll(nil); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := w.ErrorIfNotEqualLists(makeMap1()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
w = makeMap1()
|
||||||
|
w2 := makeMap2(types.BehaviorReplace)
|
||||||
|
if err := w.AbsorbAll(w2); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if err := w2.ErrorIfNotEqualLists(w); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
w = makeMap1()
|
||||||
|
w2 = makeMap2(types.BehaviorUnspecified)
|
||||||
|
err := w.AbsorbAll(w2)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error with unspecified behavior")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -277,12 +277,6 @@ func (r *Resource) PrefixesSuffixesEquals(o ResCtx) bool {
|
|||||||
return sameEndingSubarray(r.GetNamePrefixes(), o.GetNamePrefixes()) && sameEndingSubarray(r.GetNameSuffixes(), o.GetNameSuffixes())
|
return sameEndingSubarray(r.GetNamePrefixes(), o.GetNamePrefixes()) && sameEndingSubarray(r.GetNameSuffixes(), o.GetNameSuffixes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is used to compute if a referrer could potentially be impacted
|
|
||||||
// by the change of name of a referral.
|
|
||||||
func (r *Resource) InSameKustomizeCtx(rctxm ResCtxMatcher) bool {
|
|
||||||
return rctxm(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) GetOriginalName() string {
|
func (r *Resource) GetOriginalName() string {
|
||||||
return r.originalName
|
return r.originalName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Harness manages a kustomize environment for tests.
|
// Harness manages a test environment.
|
||||||
type Harness struct {
|
type Harness struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
fSys filesys.FileSystem
|
fSys filesys.FileSystem
|
||||||
@@ -72,15 +72,12 @@ func (th Harness) MakeDefaultOptions() krusty.Options {
|
|||||||
|
|
||||||
// This has no impact on Builtin plugins, as they are always enabled.
|
// This has no impact on Builtin plugins, as they are always enabled.
|
||||||
func (th Harness) MakeOptionsPluginsDisabled() krusty.Options {
|
func (th Harness) MakeOptionsPluginsDisabled() krusty.Options {
|
||||||
return krusty.Options{
|
return *krusty.MakeDefaultOptions()
|
||||||
LoadRestrictions: types.LoadRestrictionsRootOnly,
|
|
||||||
PluginConfig: konfig.DisabledPluginConfig(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enables use of non-builtin plugins.
|
// Enables use of non-builtin plugins.
|
||||||
func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
|
func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
|
||||||
c, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
|
pc, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "unable to find plugin root") {
|
if strings.Contains(err.Error(), "unable to find plugin root") {
|
||||||
th.t.Log(
|
th.t.Log(
|
||||||
@@ -89,10 +86,9 @@ func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
|
|||||||
}
|
}
|
||||||
th.t.Fatal(err)
|
th.t.Fatal(err)
|
||||||
}
|
}
|
||||||
return krusty.Options{
|
o := *krusty.MakeDefaultOptions()
|
||||||
LoadRestrictions: types.LoadRestrictionsRootOnly,
|
o.PluginConfig = pc
|
||||||
PluginConfig: c,
|
return o
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run, failing on error.
|
// Run, failing on error.
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ var (
|
|||||||
ExitOnError = &commands.ExitOnError
|
ExitOnError = &commands.ExitOnError
|
||||||
)
|
)
|
||||||
|
|
||||||
// AddCommands adds the cfg, fn and live commands to kustomize.
|
// AddCommands adds the cfg and fn commands to kustomize.
|
||||||
func AddCommands(root *cobra.Command, name string) *cobra.Command {
|
func AddCommands(root *cobra.Command, name string) *cobra.Command {
|
||||||
commands.ExitOnError = true
|
commands.ExitOnError = true
|
||||||
|
|
||||||
@@ -48,7 +48,6 @@ func AddCommands(root *cobra.Command, name string) *cobra.Command {
|
|||||||
|
|
||||||
root.AddCommand(GetCfg(name))
|
root.AddCommand(GetCfg(name))
|
||||||
root.AddCommand(GetFn(name))
|
root.AddCommand(GetFn(name))
|
||||||
root.AddCommand(GetLive(name))
|
|
||||||
|
|
||||||
root.AddCommand(&cobra.Command{
|
root.AddCommand(&cobra.Command{
|
||||||
Use: "docs-merge",
|
Use: "docs-merge",
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func GetLive(name string) *cobra.Command {
|
|||||||
|
|
||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
applyCmd,
|
applyCmd,
|
||||||
initcmd.NewCmdInit(ioStreams),
|
initcmd.NewCmdInit(f, ioStreams),
|
||||||
preview.GetPreviewRunner(f, ioStreams).Command,
|
preview.GetPreviewRunner(f, ioStreams).Command,
|
||||||
diff.NewCmdDiff(f, ioStreams),
|
diff.NewCmdDiff(f, ioStreams),
|
||||||
destroy.GetDestroyRunner(f, ioStreams).Command)
|
destroy.GetDestroyRunner(f, ioStreams).Command)
|
||||||
|
|||||||
@@ -12,3 +12,13 @@ import (
|
|||||||
var GetOpenAPIFile = func(args []string) (string, error) {
|
var GetOpenAPIFile = func(args []string) (string, error) {
|
||||||
return filepath.Join(args[0], "Krmfile"), nil
|
return filepath.Join(args[0], "Krmfile"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAPIFileName returns the name of the file with openAPI definitions
|
||||||
|
// uses OpenAPIFile function to derive it
|
||||||
|
func OpenAPIFileName() (string, error) {
|
||||||
|
openAPIFileName, err := GetOpenAPIFile([]string{"."})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return openAPIFileName, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ require (
|
|||||||
k8s.io/cli-runtime v0.17.3
|
k8s.io/cli-runtime v0.17.3
|
||||||
k8s.io/client-go v0.17.3
|
k8s.io/client-go v0.17.3
|
||||||
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
|
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
|
||||||
sigs.k8s.io/cli-utils v0.19.0
|
sigs.k8s.io/cli-utils v0.20.1
|
||||||
sigs.k8s.io/kustomize/kyaml v0.6.0
|
sigs.k8s.io/kustomize/kyaml v0.8.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml
|
|
||||||
|
|||||||
@@ -283,6 +283,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||||
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
@@ -615,8 +617,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
|
|||||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
|
||||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||||
sigs.k8s.io/cli-utils v0.19.0 h1:lAoR5okhSV/dIusodaQp5VbDpHMcKnvjqKYHU+AB3a4=
|
sigs.k8s.io/cli-utils v0.20.1 h1:S3IM4Rmely9oWNAeytx46PhngTwdRxsJ4ixtoGWPc3o=
|
||||||
sigs.k8s.io/cli-utils v0.19.0/go.mod h1:B7KdqkSkHNIUn3cFbaR4aKUZMKtr+Benboi1w/HW/Fg=
|
sigs.k8s.io/cli-utils v0.20.1/go.mod h1:fQkEMmLXduMNmUf1+dgQ5GRJa4OrrLMw3qzAN8YXAU8=
|
||||||
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
|
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
|
||||||
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
|
||||||
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
|
||||||
|
|||||||
@@ -4,9 +4,12 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -31,6 +34,8 @@ func NewAnnotateRunner(parent string) *AnnotateRunner {
|
|||||||
c.Flags().StringVar(&r.Name, "name", "", "Resource name to annotate")
|
c.Flags().StringVar(&r.Name, "name", "", "Resource name to annotate")
|
||||||
c.Flags().StringVar(&r.Namespace, "namespace", "", "Resource namespace to annotate")
|
c.Flags().StringVar(&r.Namespace, "namespace", "", "Resource namespace to annotate")
|
||||||
c.Flags().StringSliceVar(&r.Values, "kv", []string{}, "annotation as KEY=VALUE")
|
c.Flags().StringSliceVar(&r.Values, "kv", []string{}, "annotation as KEY=VALUE")
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
||||||
|
"add annotations recursively in all the nested subpackages")
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,13 +44,14 @@ func AnnotateCommand(parent string) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AnnotateRunner struct {
|
type AnnotateRunner struct {
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
Values []string
|
Values []string
|
||||||
Kind string
|
Kind string
|
||||||
Name string
|
Name string
|
||||||
ApiVersion string
|
ApiVersion string
|
||||||
Namespace string
|
Namespace string
|
||||||
Path string
|
Path string
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error {
|
func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error {
|
||||||
@@ -55,16 +61,54 @@ func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
rw := &kio.ByteReadWriter{Reader: c.InOrStdin(), Writer: c.OutOrStdout()}
|
rw := &kio.ByteReadWriter{Reader: c.InOrStdin(), Writer: c.OutOrStdout()}
|
||||||
input = []kio.Reader{rw}
|
input = []kio.Reader{rw}
|
||||||
output = []kio.Writer{rw}
|
output = []kio.Writer{rw}
|
||||||
} else {
|
|
||||||
rw := &kio.LocalPackageReadWriter{PackagePath: args[0], NoDeleteFiles: true}
|
return handleError(c, kio.Pipeline{
|
||||||
input = []kio.Reader{rw}
|
Inputs: input,
|
||||||
output = []kio.Writer{rw}
|
Filters: []kio.Filter{r},
|
||||||
|
Outputs: output,
|
||||||
|
}.Execute())
|
||||||
}
|
}
|
||||||
return handleError(c, kio.Pipeline{
|
|
||||||
|
e := executeCmdOnPkgs{
|
||||||
|
writer: c.OutOrStdout(),
|
||||||
|
needOpenAPI: false,
|
||||||
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.execute()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *AnnotateRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rw := &kio.LocalPackageReadWriter{PackagePath: pkgPath, NoDeleteFiles: true, PackageFileName: openAPIFileName}
|
||||||
|
input := []kio.Reader{rw}
|
||||||
|
output := []kio.Writer{rw}
|
||||||
|
err = kio.Pipeline{
|
||||||
Inputs: input,
|
Inputs: input,
|
||||||
Filters: []kio.Filter{r},
|
Filters: []kio.Filter{r},
|
||||||
Outputs: output,
|
Outputs: output,
|
||||||
}.Execute())
|
}.Execute()
|
||||||
|
if err != nil {
|
||||||
|
// return err if there is only package
|
||||||
|
if !r.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if there are multiple packages to annotate
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(w, "added annotations in the package\n")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *AnnotateRunner) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (r *AnnotateRunner) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAnnotateCommand(t *testing.T) {
|
func TestAnnotateCommand(t *testing.T) {
|
||||||
@@ -486,3 +488,77 @@ spec:
|
|||||||
replicas: 3
|
replicas: 3
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestAnnotateSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "annotate-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
args: []string{"--kv", "foo=bar", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
added annotations in the package
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
added annotations in the package
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "annotate-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"--kv", "foo=bar"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
added annotations in the package
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "annotate-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"--kv", "foo=bar"},
|
||||||
|
expected: `${baseDir}/mysql/storage/
|
||||||
|
added annotations in the package
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := NewAnnotateRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -19,15 +21,14 @@ import (
|
|||||||
func GetCatRunner(name string) *CatRunner {
|
func GetCatRunner(name string) *CatRunner {
|
||||||
r := &CatRunner{}
|
r := &CatRunner{}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "cat DIR...",
|
Use: "cat DIR",
|
||||||
Short: commands.CatShort,
|
Short: commands.CatShort,
|
||||||
Long: commands.CatLong,
|
Long: commands.CatLong,
|
||||||
Example: commands.CatExamples,
|
Example: commands.CatExamples,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
|
Args: cobra.MaximumNArgs(1),
|
||||||
}
|
}
|
||||||
fixDocs(name, c)
|
fixDocs(name, c)
|
||||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
|
||||||
"also print resources from subpackages.")
|
|
||||||
c.Flags().BoolVar(&r.Format, "format", true,
|
c.Flags().BoolVar(&r.Format, "format", true,
|
||||||
"format resource config yaml before printing.")
|
"format resource config yaml before printing.")
|
||||||
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", false,
|
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", false,
|
||||||
@@ -49,6 +50,8 @@ func GetCatRunner(name string) *CatRunner {
|
|||||||
"if true, exclude non-local-config in the output.")
|
"if true, exclude non-local-config in the output.")
|
||||||
c.Flags().StringVar(&r.OutputDest, "dest", "",
|
c.Flags().StringVar(&r.OutputDest, "dest", "",
|
||||||
"if specified, write output to a file rather than stdout")
|
"if specified, write output to a file rather than stdout")
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
|
||||||
|
"print resources recursively in all the nested subpackages")
|
||||||
r.Command = c
|
r.Command = c
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -59,7 +62,6 @@ func CatCommand(name string) *cobra.Command {
|
|||||||
|
|
||||||
// CatRunner contains the run function
|
// CatRunner contains the run function
|
||||||
type CatRunner struct {
|
type CatRunner struct {
|
||||||
IncludeSubpackages bool
|
|
||||||
Format bool
|
Format bool
|
||||||
KeepAnnotations bool
|
KeepAnnotations bool
|
||||||
WrapKind string
|
WrapKind string
|
||||||
@@ -71,56 +73,102 @@ type CatRunner struct {
|
|||||||
IncludeLocal bool
|
IncludeLocal bool
|
||||||
ExcludeNonLocal bool
|
ExcludeNonLocal bool
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
||||||
// if there is a function-config specified, emit it
|
var writer = c.OutOrStdout()
|
||||||
|
if r.OutputDest != "" {
|
||||||
|
o, err := os.Create(r.OutputDest)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
defer o.Close()
|
||||||
|
writer = o
|
||||||
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
input := &kio.ByteReader{Reader: c.InOrStdin()}
|
||||||
|
// if there is a function-config specified, emit it
|
||||||
|
outputs, err := r.out(writer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return handleError(c, kio.Pipeline{Inputs: []kio.Reader{input}, Filters: r.catFilters(), Outputs: outputs}.Execute())
|
||||||
|
}
|
||||||
|
|
||||||
|
e := executeCmdOnPkgs{
|
||||||
|
writer: writer,
|
||||||
|
needOpenAPI: false,
|
||||||
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
skipPkgPathPrint: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CatRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
input := kio.LocalPackageReader{PackagePath: pkgPath, PackageFileName: openAPIFileName}
|
||||||
|
outputs, err := r.out(w)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{input},
|
||||||
|
Filters: r.catFilters(),
|
||||||
|
Outputs: outputs,
|
||||||
|
}.Execute()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// return err if there is only package
|
||||||
|
if !r.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if there are multiple packages to annotate
|
||||||
|
fmt.Fprintf(w, "%s in package %q\n", err.Error(), pkgPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "---")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CatRunner) catFilters() []kio.Filter {
|
||||||
|
var fltrs []kio.Filter
|
||||||
|
// don't include reconcilers
|
||||||
|
fltrs = append(fltrs, &filters.IsLocalConfig{
|
||||||
|
IncludeLocalConfig: r.IncludeLocal,
|
||||||
|
ExcludeNonLocalConfig: r.ExcludeNonLocal,
|
||||||
|
})
|
||||||
|
if r.Format {
|
||||||
|
fltrs = append(fltrs, filters.FormatFilter{})
|
||||||
|
}
|
||||||
|
if r.StripComments {
|
||||||
|
fltrs = append(fltrs, filters.StripCommentsFilter{})
|
||||||
|
}
|
||||||
|
return fltrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CatRunner) out(w io.Writer) ([]kio.Writer, error) {
|
||||||
|
var outputs []kio.Writer
|
||||||
var functionConfig *yaml.RNode
|
var functionConfig *yaml.RNode
|
||||||
if r.FunctionConfig != "" {
|
if r.FunctionConfig != "" {
|
||||||
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig,
|
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig,
|
||||||
OmitReaderAnnotations: !r.KeepAnnotations}.Read()
|
OmitReaderAnnotations: !r.KeepAnnotations}.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return outputs, err
|
||||||
}
|
}
|
||||||
if len(configs) != 1 {
|
if len(configs) != 1 {
|
||||||
return fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
|
return outputs, fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
|
||||||
}
|
}
|
||||||
functionConfig = configs[0]
|
functionConfig = configs[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputs []kio.Reader
|
|
||||||
for _, a := range args {
|
|
||||||
inputs = append(inputs, kio.LocalPackageReader{
|
|
||||||
PackagePath: a,
|
|
||||||
IncludeSubpackages: r.IncludeSubpackages,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if len(inputs) == 0 {
|
|
||||||
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
|
|
||||||
}
|
|
||||||
var fltr []kio.Filter
|
|
||||||
// don't include reconcilers
|
|
||||||
fltr = append(fltr, &filters.IsLocalConfig{
|
|
||||||
IncludeLocalConfig: r.IncludeLocal,
|
|
||||||
ExcludeNonLocalConfig: r.ExcludeNonLocal,
|
|
||||||
})
|
|
||||||
if r.Format {
|
|
||||||
fltr = append(fltr, filters.FormatFilter{})
|
|
||||||
}
|
|
||||||
if r.StripComments {
|
|
||||||
fltr = append(fltr, filters.StripCommentsFilter{})
|
|
||||||
}
|
|
||||||
|
|
||||||
var out = c.OutOrStdout()
|
|
||||||
if r.OutputDest != "" {
|
|
||||||
o, err := os.Create(r.OutputDest)
|
|
||||||
if err != nil {
|
|
||||||
return handleError(c, errors.Wrap(err))
|
|
||||||
}
|
|
||||||
defer o.Close()
|
|
||||||
out = o
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove this annotation explicitly, the ByteWriter won't clear it by
|
// remove this annotation explicitly, the ByteWriter won't clear it by
|
||||||
// default because it doesn't set it
|
// default because it doesn't set it
|
||||||
clear := []string{"config.kubernetes.io/path"}
|
clear := []string{"config.kubernetes.io/path"}
|
||||||
@@ -128,9 +176,8 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
clear = nil
|
clear = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var outputs []kio.Writer
|
|
||||||
outputs = append(outputs, kio.ByteWriter{
|
outputs = append(outputs, kio.ByteWriter{
|
||||||
Writer: out,
|
Writer: w,
|
||||||
KeepReaderAnnotations: r.KeepAnnotations,
|
KeepReaderAnnotations: r.KeepAnnotations,
|
||||||
WrappingKind: r.WrapKind,
|
WrappingKind: r.WrapKind,
|
||||||
WrappingAPIVersion: r.WrapApiVersion,
|
WrappingAPIVersion: r.WrapApiVersion,
|
||||||
@@ -139,5 +186,5 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
ClearAnnotations: clear,
|
ClearAnnotations: clear,
|
||||||
})
|
})
|
||||||
|
|
||||||
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: fltr, Outputs: outputs}.Execute())
|
return outputs, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(pwittrock): write tests for reading / writing ResourceLists
|
// TODO(pwittrock): write tests for reading / writing ResourceLists
|
||||||
@@ -112,7 +115,7 @@ metadata:
|
|||||||
app: nginx
|
app: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
`, b.String()) {
|
---`, b.String()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,8 +171,7 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
app: nginx
|
app: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3`), 0600)
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -223,7 +225,7 @@ metadata:
|
|||||||
app: nginx
|
app: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
`, b.String()) {
|
---`, b.String()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,7 +307,7 @@ metadata:
|
|||||||
image: gcr.io/example/reconciler:v1
|
image: gcr.io/example/reconciler:v1
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
`, b.String()) {
|
---`, b.String()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -362,8 +364,7 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
app: nginx
|
app: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3`), 0600)
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -420,7 +421,7 @@ metadata:
|
|||||||
app: nginx
|
app: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
`, string(actual)) {
|
---`, string(actual)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,8 +478,7 @@ metadata:
|
|||||||
annotations:
|
annotations:
|
||||||
app: nginx
|
app: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3`), 0600)
|
||||||
`), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -536,7 +536,132 @@ metadata:
|
|||||||
app: nginx
|
app: nginx
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
`, string(actual)) {
|
---`, string(actual)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCatSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "cat-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
expected: `# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mysql-deployment
|
||||||
|
namespace: myspace
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:1.7.9
|
||||||
|
---
|
||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: storage-deployment
|
||||||
|
namespace: myspace
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: storage
|
||||||
|
image: storage:1.7.7
|
||||||
|
---`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cat-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
args: []string{"-R=false"},
|
||||||
|
packagePath: "mysql",
|
||||||
|
expected: `# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mysql-deployment
|
||||||
|
namespace: myspace
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:1.7.9
|
||||||
|
---`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cat-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"-R=false"},
|
||||||
|
expected: `# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: storage-deployment
|
||||||
|
namespace: myspace
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: storage
|
||||||
|
image: storage:1.7.7
|
||||||
|
---`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.GetCatRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-openapi/spec"
|
"github.com/go-openapi/spec"
|
||||||
@@ -13,9 +16,7 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||||
)
|
)
|
||||||
@@ -59,6 +60,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
|||||||
set.Flags().StringVar(&r.SchemaPath, "schema-path", "",
|
set.Flags().StringVar(&r.SchemaPath, "schema-path", "",
|
||||||
`openAPI schema file path for setter constraints -- file content `+
|
`openAPI schema file path for setter constraints -- file content `+
|
||||||
`e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`)
|
`e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`)
|
||||||
|
set.Flags().BoolVarP(&r.CreateSetter.RecurseSubPackages, "recurse-subpackages", "R", false,
|
||||||
|
"creates setter recursively in all the nested subpackages")
|
||||||
set.Flags().MarkHidden("version")
|
set.Flags().MarkHidden("version")
|
||||||
fixDocs(parent, set)
|
fixDocs(parent, set)
|
||||||
r.Command = set
|
r.Command = set
|
||||||
@@ -78,7 +81,7 @@ type CreateSetterRunner struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
|
||||||
return handleError(c, r.set(c, args))
|
return handleError(c, r.createSetter(c, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
@@ -111,42 +114,6 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if setterVersion == "v2" {
|
if setterVersion == "v2" {
|
||||||
var err error
|
|
||||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if substitution with same name exists and throw error
|
|
||||||
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + r.CreateSetter.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
subst, _ := openapi.Resolve(&ref)
|
|
||||||
// if substitution already exists with the input setter name, throw error
|
|
||||||
if subst != nil {
|
|
||||||
return errors.Errorf("substitution with name %s already exists, "+
|
|
||||||
"substitution and setter can't have same name", r.CreateSetter.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if setter with same name exists and throw error
|
|
||||||
ref, err = spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + r.CreateSetter.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
setter, _ := openapi.Resolve(&ref)
|
|
||||||
// if setter already exists with the input setter name, throw error
|
|
||||||
if setter != nil {
|
|
||||||
return errors.Errorf("setter with name %s already exists, "+
|
|
||||||
"if you want to modify it, please delete the existing setter and recreate it", r.CreateSetter.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.CreateSetter.Description = r.Set.SetPartialField.Description
|
r.CreateSetter.Description = r.Set.SetPartialField.Description
|
||||||
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
|
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
|
||||||
r.CreateSetter.Type = r.Set.SetPartialField.Type
|
r.CreateSetter.Type = r.Set.SetPartialField.Type
|
||||||
@@ -210,9 +177,20 @@ func (r *CreateSetterRunner) processSchema() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
|
func (r *CreateSetterRunner) createSetter(c *cobra.Command, args []string) error {
|
||||||
if setterVersion == "v2" {
|
if setterVersion == "v2" {
|
||||||
return r.CreateSetter.Create(r.OpenAPIFile, args[0])
|
e := executeCmdOnPkgs{
|
||||||
|
needOpenAPI: true,
|
||||||
|
writer: c.OutOrStdout(),
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
recurseSubPackages: r.CreateSetter.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
}
|
||||||
|
err := e.execute()
|
||||||
|
if err != nil {
|
||||||
|
return handleError(c, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := &kio.LocalPackageReadWriter{PackagePath: args[0]}
|
rw := &kio.LocalPackageReadWriter{PackagePath: args[0]}
|
||||||
@@ -226,6 +204,41 @@ func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *CreateSetterRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.CreateSetter = settersutil.SetterCreator{
|
||||||
|
Name: r.CreateSetter.Name,
|
||||||
|
SetBy: r.CreateSetter.SetBy,
|
||||||
|
Description: r.CreateSetter.Description,
|
||||||
|
Type: r.CreateSetter.Type,
|
||||||
|
Schema: r.CreateSetter.Schema,
|
||||||
|
FieldName: r.CreateSetter.FieldName,
|
||||||
|
FieldValue: r.CreateSetter.FieldValue,
|
||||||
|
Required: r.CreateSetter.Required,
|
||||||
|
RecurseSubPackages: r.CreateSetter.RecurseSubPackages,
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
|
||||||
|
ResourcesPath: pkgPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.CreateSetter.Create()
|
||||||
|
if err != nil {
|
||||||
|
// return err if RecurseSubPackages is false
|
||||||
|
if !r.CreateSetter.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if RecurseSubPackages is true
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "created setter %q\n", r.CreateSetter.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// schemaFromFile reads the contents from schemaPath and returns schema
|
// schemaFromFile reads the contents from schemaPath and returns schema
|
||||||
func schemaFromFile(schemaPath string) (*spec.Schema, error) {
|
func schemaFromFile(schemaPath string) (*spec.Schema, error) {
|
||||||
sc := &spec.Schema{}
|
sc := &spec.Schema{}
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "replicas"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -81,6 +83,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "replicas"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -122,7 +125,7 @@ openAPI:
|
|||||||
- marker: ${my-tag-setter}
|
- marker: ${my-tag-setter}
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||||
`,
|
`,
|
||||||
err: "substitution with name my-image already exists, substitution and setter can't have same name",
|
err: `substitution with name "my-image" already exists, substitution and setter can't have same name`,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -140,7 +143,7 @@ openAPI:
|
|||||||
name: my-image
|
name: my-image
|
||||||
value: "nginx"
|
value: "nginx"
|
||||||
`,
|
`,
|
||||||
err: "setter with name my-image already exists, if you want to modify it, please delete the existing setter and recreate it",
|
err: `setter with name "my-image" already exists, if you want to modify it, please delete the existing setter and recreate it`,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -159,6 +162,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "replicas"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -200,6 +204,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter replicas`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -254,6 +259,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "list"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -354,6 +360,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter replicas`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -400,6 +407,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "replicas"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -437,6 +445,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "foo.bar"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -587,6 +596,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "replicas"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -712,6 +722,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created setter "replicas"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -742,13 +753,14 @@ spec:
|
|||||||
openapi.ResetOpenAPI()
|
openapi.ResetOpenAPI()
|
||||||
defer openapi.ResetOpenAPI()
|
defer openapi.ResetOpenAPI()
|
||||||
|
|
||||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer os.Remove(f.Name())
|
defer os.RemoveAll(baseDir)
|
||||||
|
|
||||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
f := filepath.Join(baseDir, "Krmfile")
|
||||||
|
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -768,13 +780,7 @@ spec:
|
|||||||
test.args = append(test.args, "--schema-path", sch.Name())
|
test.args = append(test.args, "--schema-path", sch.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
old := ext.GetOpenAPIFile
|
r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
|
||||||
defer func() { ext.GetOpenAPIFile = old }()
|
|
||||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -787,7 +793,7 @@ spec:
|
|||||||
runner := commands.NewCreateSetterRunner("")
|
runner := commands.NewCreateSetterRunner("")
|
||||||
out := &bytes.Buffer{}
|
out := &bytes.Buffer{}
|
||||||
runner.Command.SetOut(out)
|
runner.Command.SetOut(out)
|
||||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
runner.Command.SetArgs(append([]string{baseDir}, test.args...))
|
||||||
err = runner.Command.Execute()
|
err = runner.Command.Execute()
|
||||||
if test.err != "" {
|
if test.err != "" {
|
||||||
if !assert.NotNil(t, err) {
|
if !assert.NotNil(t, err) {
|
||||||
@@ -801,7 +807,14 @@ spec:
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, test.out, out.String()) {
|
expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expectedOut, "\\", "/", -1)
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(out.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -815,7 +828,7 @@ spec:
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
actualOpenAPI, err := ioutil.ReadFile(f)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -827,3 +840,92 @@ spec:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateSetterSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "create-setter-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
args: []string{"namespace", "myspace", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
created setter "namespace"
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
created setter "namespace"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create-setter-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"namespace", "myspace"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
created setter "namespace"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create-setter-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"namespace", "myspace"},
|
||||||
|
expected: `${baseDir}/mysql/storage/
|
||||||
|
created setter "namespace"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create-setter-already-exists",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"namespace", "myspace", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it
|
||||||
|
|
||||||
|
${baseDir}/mysql/nosetters/
|
||||||
|
created setter "namespace"
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.NewCreateSetterRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/go-openapi/spec"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,10 +17,10 @@ import (
|
|||||||
func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
|
func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
|
||||||
r := &CreateSubstitutionRunner{}
|
r := &CreateSubstitutionRunner{}
|
||||||
cs := &cobra.Command{
|
cs := &cobra.Command{
|
||||||
Use: "create-subst DIR NAME",
|
Use: "create-subst DIR NAME",
|
||||||
Args: cobra.ExactArgs(2),
|
Args: cobra.ExactArgs(2),
|
||||||
PreRunE: r.preRunE,
|
PreRun: r.preRun,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
}
|
}
|
||||||
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
|
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
|
||||||
"name of the field to set -- e.g. --field image")
|
"name of the field to set -- e.g. --field image")
|
||||||
@@ -30,6 +28,8 @@ func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
|
|||||||
"value of the field to create substitution for -- e.g. --field-value nginx:0.1.0")
|
"value of the field to create substitution for -- e.g. --field-value nginx:0.1.0")
|
||||||
cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "",
|
cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "",
|
||||||
`substitution pattern -- e.g. --pattern \${my-image-setter}:\${my-tag-setter}`)
|
`substitution pattern -- e.g. --pattern \${my-image-setter}:\${my-tag-setter}`)
|
||||||
|
cs.Flags().BoolVarP(&r.CreateSubstitution.RecurseSubPackages, "recurse-subpackages", "R", false,
|
||||||
|
"creates substitution recursively in all the nested subpackages")
|
||||||
_ = cs.MarkFlagRequired("pattern")
|
_ = cs.MarkFlagRequired("pattern")
|
||||||
_ = cs.MarkFlagRequired("field-value")
|
_ = cs.MarkFlagRequired("field-value")
|
||||||
fixDocs(parent, cs)
|
fixDocs(parent, cs)
|
||||||
@@ -49,49 +49,52 @@ type CreateSubstitutionRunner struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
||||||
return handleError(c, r.CreateSubstitution.Create(r.OpenAPIFile, args[0]))
|
e := executeCmdOnPkgs{
|
||||||
}
|
needOpenAPI: true,
|
||||||
|
writer: c.OutOrStdout(),
|
||||||
func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
|
rootPkgPath: args[0],
|
||||||
var err error
|
recurseSubPackages: r.CreateSubstitution.RecurseSubPackages,
|
||||||
r.CreateSubstitution.Name = args[1]
|
cmdRunner: r,
|
||||||
|
}
|
||||||
|
err := e.execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return handleError(c, err)
|
||||||
}
|
|
||||||
|
|
||||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if substitution with same name exists and throw error
|
|
||||||
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + r.CreateSubstitution.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
subst, _ := openapi.Resolve(&ref)
|
|
||||||
// if substitution already exists with the input substitution name, throw error
|
|
||||||
if subst != nil {
|
|
||||||
return errors.Errorf("substitution with name %s already exists", r.CreateSubstitution.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if setter with same name exists and throw error
|
|
||||||
ref, err = spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + r.CreateSubstitution.Name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
setter, _ := openapi.Resolve(&ref)
|
|
||||||
// if setter already exists with input substitution name, throw error
|
|
||||||
if setter != nil {
|
|
||||||
return errors.Errorf(fmt.Sprintf("setter with name %s already exists, "+
|
|
||||||
"substitution and setter can't have same name", r.CreateSubstitution.Name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *CreateSubstitutionRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.CreateSubstitution = settersutil.SubstitutionCreator{
|
||||||
|
Name: r.CreateSubstitution.Name,
|
||||||
|
FieldName: r.CreateSubstitution.FieldName,
|
||||||
|
FieldValue: r.CreateSubstitution.FieldValue,
|
||||||
|
RecurseSubPackages: r.CreateSubstitution.RecurseSubPackages,
|
||||||
|
Pattern: r.CreateSubstitution.Pattern,
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
|
||||||
|
ResourcesPath: pkgPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.CreateSubstitution.Create()
|
||||||
|
if err != nil {
|
||||||
|
// return err if RecurseSubPackages is false
|
||||||
|
if !r.CreateSubstitution.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if RecurseSubPackages is true
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "created substitution %q\n", r.CreateSubstitution.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CreateSubstitutionRunner) preRun(c *cobra.Command, args []string) {
|
||||||
|
r.CreateSubstitution.Name = args[1]
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ openAPI:
|
|||||||
name: my-tag-setter
|
name: my-tag-setter
|
||||||
value: "1.7.9"
|
value: "1.7.9"
|
||||||
`,
|
`,
|
||||||
|
out: `created substitution "my-image-subst"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -123,7 +125,7 @@ openAPI:
|
|||||||
- marker: ${my-tag-setter}
|
- marker: ${my-tag-setter}
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||||
`,
|
`,
|
||||||
err: "substitution with name my-image already exists",
|
err: `substitution with name "my-image" already exists`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error if setter with same name exists",
|
name: "error if setter with same name exists",
|
||||||
@@ -140,7 +142,7 @@ openAPI:
|
|||||||
name: my-image
|
name: my-image
|
||||||
value: "nginx"
|
value: "nginx"
|
||||||
`,
|
`,
|
||||||
err: "setter with name my-image already exists, substitution and setter can't have same name",
|
err: `setter with name "my-image" already exists, substitution and setter can't have same name`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "substitution and create setters 1",
|
name: "substitution and create setters 1",
|
||||||
@@ -165,6 +167,7 @@ spec:
|
|||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
`,
|
`,
|
||||||
|
out: `created substitution "my-image-subst"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -253,6 +256,7 @@ openAPI:
|
|||||||
- marker: ${my-tag-setter}
|
- marker: ${my-tag-setter}
|
||||||
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||||
`,
|
`,
|
||||||
|
out: `created substitution "my-nested-subst"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -341,7 +345,7 @@ spec:
|
|||||||
substVal: prefix-1234
|
substVal: prefix-1234
|
||||||
|
|
||||||
`,
|
`,
|
||||||
err: "setters must have different name than the substitution: foo",
|
err: `setters must have different name than the substitution: foo`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i := range tests {
|
for i := range tests {
|
||||||
@@ -351,22 +355,18 @@ spec:
|
|||||||
openapi.ResetOpenAPI()
|
openapi.ResetOpenAPI()
|
||||||
defer openapi.ResetOpenAPI()
|
defer openapi.ResetOpenAPI()
|
||||||
|
|
||||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer os.Remove(f.Name())
|
defer os.RemoveAll(baseDir)
|
||||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
f := filepath.Join(baseDir, "Krmfile")
|
||||||
|
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
old := ext.GetOpenAPIFile
|
|
||||||
defer func() { ext.GetOpenAPIFile = old }()
|
|
||||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -379,7 +379,7 @@ spec:
|
|||||||
runner := commands.NewCreateSubstitutionRunner("")
|
runner := commands.NewCreateSubstitutionRunner("")
|
||||||
out := &bytes.Buffer{}
|
out := &bytes.Buffer{}
|
||||||
runner.Command.SetOut(out)
|
runner.Command.SetOut(out)
|
||||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
runner.Command.SetArgs(append([]string{baseDir}, test.args...))
|
||||||
err = runner.Command.Execute()
|
err = runner.Command.Execute()
|
||||||
|
|
||||||
if test.err != "" {
|
if test.err != "" {
|
||||||
@@ -393,7 +393,14 @@ spec:
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, test.out, out.String()) {
|
expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expectedOut, "\\", "/", -1)
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(out.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
if !assert.Contains(t, actualNormalized, expectedNormalized) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,7 +414,7 @@ spec:
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
actualOpenAPI, err := ioutil.ReadFile(f)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -419,3 +426,89 @@ spec:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateSubstSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "create-subst-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
created substitution "image-tag"
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
created substitution "image-tag"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create-subst-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
created substitution "image-tag"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create-subst-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"image-tag", "--field-value", "storage:1.7.9", "--pattern", "${image}:${tag}"},
|
||||||
|
expected: `${baseDir}/mysql/storage/
|
||||||
|
created substitution "image-tag"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create-subst-already-exists",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
substitution with name "image-tag" already exists
|
||||||
|
|
||||||
|
${baseDir}/mysql/nosetters/
|
||||||
|
created substitution "image-tag"
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
created substitution "image-tag"`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.NewCreateSubstitutionRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,6 +27,8 @@ func NewDeleteSetterRunner(parent string) *DeleteSetterRunner {
|
|||||||
PreRunE: r.preRunE,
|
PreRunE: r.preRunE,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
}
|
}
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
||||||
|
"deletes setter recursively in all the nested subpackages")
|
||||||
fixDocs(parent, c)
|
fixDocs(parent, c)
|
||||||
r.Command = c
|
r.Command = c
|
||||||
|
|
||||||
@@ -35,9 +40,10 @@ func DeleteSetterCommand(parent string) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DeleteSetterRunner struct {
|
type DeleteSetterRunner struct {
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
DeleteSetter settersutil.DeleterCreator
|
DeleteSetter settersutil.DeleterCreator
|
||||||
OpenAPIFile string
|
OpenAPIFile string
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
@@ -50,17 +56,49 @@ func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeleteSetterRunner) runE(c *cobra.Command, args []string) error {
|
func (r *DeleteSetterRunner) runE(c *cobra.Command, args []string) error {
|
||||||
return handleError(c, r.delete(c, args))
|
e := executeCmdOnPkgs{
|
||||||
|
needOpenAPI: true,
|
||||||
|
writer: c.OutOrStdout(),
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
}
|
||||||
|
err := e.execute()
|
||||||
|
if err != nil {
|
||||||
|
return handleError(c, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeleteSetterRunner) delete(c *cobra.Command, args []string) error {
|
func (r *DeleteSetterRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
return r.DeleteSetter.Delete(r.OpenAPIFile, args[0])
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.DeleteSetter = settersutil.DeleterCreator{
|
||||||
|
Name: r.DeleteSetter.Name,
|
||||||
|
DefinitionPrefix: fieldmeta.SetterDefinitionPrefix,
|
||||||
|
RecurseSubPackages: r.RecurseSubPackages,
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
|
||||||
|
ResourcesPath: pkgPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.DeleteSetter.Delete()
|
||||||
|
if err != nil {
|
||||||
|
// return err if RecurseSubPackages is false
|
||||||
|
if !r.DeleteSetter.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if RecurseSubPackages is true
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "deleted setter %q\n", r.DeleteSetter.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ openAPI:
|
|||||||
value: "3"
|
value: "3"
|
||||||
setBy: me
|
setBy: me
|
||||||
`,
|
`,
|
||||||
|
out: `deleted setter "replicas-setter"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -95,6 +97,7 @@ openAPI:
|
|||||||
name: image
|
name: image
|
||||||
value: nginx
|
value: nginx
|
||||||
`,
|
`,
|
||||||
|
out: `deleted setter "replicas-setter"`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -158,6 +161,7 @@ spec:
|
|||||||
- "b"
|
- "b"
|
||||||
- "c"
|
- "c"
|
||||||
`,
|
`,
|
||||||
|
out: `deleted setter "list"`,
|
||||||
expectedResources: `
|
expectedResources: `
|
||||||
apiVersion: example.com/v1beta1
|
apiVersion: example.com/v1beta1
|
||||||
kind: Example1
|
kind: Example1
|
||||||
@@ -226,7 +230,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
replicas: 3 # {"$openapi" : "replicas-setter"}
|
replicas: 3 # {"$openapi" : "replicas-setter"}
|
||||||
`,
|
`,
|
||||||
err: `setter with name image does not exist`,
|
err: `setter "image" does not exist`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete setter used in substitution error",
|
name: "delete setter used in substitution error",
|
||||||
@@ -287,7 +291,7 @@ openAPI:
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
`,
|
`,
|
||||||
err: `setter is used in substitution image, please delete the parent substitution first`,
|
err: `setter "image-name" is used in substitution "image", please delete the parent substitution first`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i := range tests {
|
for i := range tests {
|
||||||
@@ -297,24 +301,18 @@ kind: Deployment
|
|||||||
openapi.ResetOpenAPI()
|
openapi.ResetOpenAPI()
|
||||||
defer openapi.ResetOpenAPI()
|
defer openapi.ResetOpenAPI()
|
||||||
|
|
||||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer os.Remove(f.Name())
|
defer os.RemoveAll(baseDir)
|
||||||
|
f := filepath.Join(baseDir, "Krmfile")
|
||||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
old := ext.GetOpenAPIFile
|
r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
|
||||||
defer func() { ext.GetOpenAPIFile = old }()
|
|
||||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -327,21 +325,29 @@ kind: Deployment
|
|||||||
runner := commands.NewDeleteSetterRunner("")
|
runner := commands.NewDeleteSetterRunner("")
|
||||||
out := &bytes.Buffer{}
|
out := &bytes.Buffer{}
|
||||||
runner.Command.SetOut(out)
|
runner.Command.SetOut(out)
|
||||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
runner.Command.SetArgs(append([]string{baseDir}, test.args...))
|
||||||
err = runner.Command.Execute()
|
err = runner.Command.Execute()
|
||||||
|
|
||||||
if test.err != "" {
|
if test.err != "" {
|
||||||
if !assert.NotNil(t, err) {
|
if !assert.NotNil(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
} else {
|
|
||||||
assert.Equal(t, err.Error(), test.err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, test.err, err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, test.out, out.String()) {
|
// normalize path format for windows
|
||||||
|
actualNorm := strings.Replace(
|
||||||
|
strings.Replace(out.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expectedOut, "\\", "/", -1)
|
||||||
|
|
||||||
|
if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNormalized) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +361,7 @@ kind: Deployment
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
actualOpenAPI, err := ioutil.ReadFile(f)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -367,3 +373,80 @@ kind: Deployment
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteSetterSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "delete-setter-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
args: []string{"namespace", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
deleted setter "namespace"
|
||||||
|
|
||||||
|
${baseDir}/mysql/nosetters/
|
||||||
|
setter "namespace" does not exist
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
deleted setter "namespace"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "delete-setter-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"namespace"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
deleted setter "namespace"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "delete-setter-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"namespace"},
|
||||||
|
expected: `${baseDir}/mysql/storage/
|
||||||
|
deleted setter "namespace"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
//defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.NewDeleteSetterRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,10 +4,13 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -20,6 +23,8 @@ func NewDeleteSubstitutionRunner(parent string) *DeleteSubstitutionRunner {
|
|||||||
PreRunE: r.preRunE,
|
PreRunE: r.preRunE,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
}
|
}
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
||||||
|
"deletes substitution recursively in all the nested subpackages")
|
||||||
fixDocs(parent, c)
|
fixDocs(parent, c)
|
||||||
r.Command = c
|
r.Command = c
|
||||||
|
|
||||||
@@ -34,6 +39,7 @@ type DeleteSubstitutionRunner struct {
|
|||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
DeleteSubstitution settersutil.DeleterCreator
|
DeleteSubstitution settersutil.DeleterCreator
|
||||||
OpenAPIFile string
|
OpenAPIFile string
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
|
func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
@@ -46,17 +52,49 @@ func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeleteSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
func (r *DeleteSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
||||||
return handleError(c, r.delete(c, args))
|
e := executeCmdOnPkgs{
|
||||||
|
needOpenAPI: true,
|
||||||
|
writer: c.OutOrStdout(),
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
}
|
||||||
|
err := e.execute()
|
||||||
|
if err != nil {
|
||||||
|
return handleError(c, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DeleteSubstitutionRunner) delete(c *cobra.Command, args []string) error {
|
func (r *DeleteSubstitutionRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
return r.DeleteSubstitution.Delete(r.OpenAPIFile, args[0])
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.DeleteSubstitution = settersutil.DeleterCreator{
|
||||||
|
Name: r.DeleteSubstitution.Name,
|
||||||
|
DefinitionPrefix: fieldmeta.SubstitutionDefinitionPrefix,
|
||||||
|
RecurseSubPackages: r.RecurseSubPackages,
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
|
||||||
|
ResourcesPath: pkgPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.DeleteSubstitution.Delete()
|
||||||
|
if err != nil {
|
||||||
|
// return err if RecurseSubPackages is false
|
||||||
|
if !r.DeleteSubstitution.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if RecurseSubPackages is true
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "deleted substitution %q\n", r.DeleteSubstitution.Name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -79,6 +80,7 @@ spec:
|
|||||||
- name: sidecar
|
- name: sidecar
|
||||||
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"}
|
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"}
|
||||||
`,
|
`,
|
||||||
|
out: `deleted substitution "my.image"`,
|
||||||
expectedResources: `
|
expectedResources: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -169,6 +171,7 @@ spec:
|
|||||||
- name: sidecar
|
- name: sidecar
|
||||||
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
||||||
`,
|
`,
|
||||||
|
out: `deleted substitution "my-image-sub"`,
|
||||||
expectedResources: `
|
expectedResources: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -297,7 +300,7 @@ openAPI:
|
|||||||
value: "3"
|
value: "3"
|
||||||
setBy: me
|
setBy: me
|
||||||
`,
|
`,
|
||||||
err: "substitution with name my-image-sub-not-present does not exist",
|
err: `substitution "my-image-sub-not-present" does not exist`,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -415,7 +418,7 @@ spec:
|
|||||||
- name: sidecar
|
- name: sidecar
|
||||||
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
||||||
`,
|
`,
|
||||||
err: "substitution is used in substitution my-nested-subst, please delete the parent substitution first",
|
err: `substitution "my-image-subst" is used in substitution "my-nested-subst", please delete the parent substitution first`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i := range tests {
|
for i := range tests {
|
||||||
@@ -425,24 +428,18 @@ spec:
|
|||||||
openapi.ResetOpenAPI()
|
openapi.ResetOpenAPI()
|
||||||
defer openapi.ResetOpenAPI()
|
defer openapi.ResetOpenAPI()
|
||||||
|
|
||||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer os.Remove(f.Name())
|
defer os.RemoveAll(baseDir)
|
||||||
|
f := filepath.Join(baseDir, "Krmfile")
|
||||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
old := ext.GetOpenAPIFile
|
r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
|
||||||
defer func() { ext.GetOpenAPIFile = old }()
|
|
||||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -455,21 +452,38 @@ spec:
|
|||||||
runner := commands.NewDeleteSubstitutionRunner("")
|
runner := commands.NewDeleteSubstitutionRunner("")
|
||||||
out := &bytes.Buffer{}
|
out := &bytes.Buffer{}
|
||||||
runner.Command.SetOut(out)
|
runner.Command.SetOut(out)
|
||||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
runner.Command.SetArgs(append([]string{baseDir}, test.args...))
|
||||||
err = runner.Command.Execute()
|
err = runner.Command.Execute()
|
||||||
|
|
||||||
if test.err != "" {
|
if test.err != "" {
|
||||||
if !assert.NotNil(t, err) {
|
if !assert.NotNil(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
} else {
|
|
||||||
assert.Equal(t, err.Error(), test.err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, test.err, err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, test.out, out.String()) {
|
// normalize path format for windows
|
||||||
|
actualNorm := strings.Replace(
|
||||||
|
strings.Replace(out.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNorm := strings.Replace(expectedOut, "\\", "/", -1)
|
||||||
|
|
||||||
|
if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNorm) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
actualOpenAPI, err := ioutil.ReadFile(f)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t,
|
||||||
|
strings.TrimSpace(test.expectedOpenAPI),
|
||||||
|
strings.TrimSpace(string(actualOpenAPI))) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,16 +494,74 @@ spec:
|
|||||||
if !assert.Equal(t,
|
if !assert.Equal(t,
|
||||||
strings.TrimSpace(test.expectedResources),
|
strings.TrimSpace(test.expectedResources),
|
||||||
strings.TrimSpace(string(actualResources))) {
|
strings.TrimSpace(string(actualResources))) {
|
||||||
return
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
})
|
||||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
}
|
||||||
if !assert.NoError(t, err) {
|
}
|
||||||
t.FailNow()
|
|
||||||
}
|
func TestDeleteSubstitutionSubPackages(t *testing.T) {
|
||||||
if !assert.Equal(t,
|
var tests = []struct {
|
||||||
strings.TrimSpace(test.expectedOpenAPI),
|
name string
|
||||||
strings.TrimSpace(string(actualOpenAPI))) {
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "delete-substitution-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
args: []string{"image-tag", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
deleted substitution "image-tag"
|
||||||
|
|
||||||
|
${baseDir}/mysql/nosetters/
|
||||||
|
substitution "image-tag" does not exist
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
substitution "image-tag" does not exist
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "delete-setter-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"image-tag"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
deleted substitution "image-tag"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
//defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.NewDeleteSubstitutionRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
@@ -34,6 +35,8 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
|
|||||||
"output as github markdown")
|
"output as github markdown")
|
||||||
c.Flags().BoolVar(&r.IncludeSubst, "include-subst", false,
|
c.Flags().BoolVar(&r.IncludeSubst, "include-subst", false,
|
||||||
"include substitutions in the output")
|
"include substitutions in the output")
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
|
||||||
|
"list setters recursively in all the nested subpackages")
|
||||||
fixDocs(parent, c)
|
fixDocs(parent, c)
|
||||||
r.Command = c
|
r.Command = c
|
||||||
return r
|
return r
|
||||||
@@ -44,11 +47,12 @@ func ListSettersCommand(parent string) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ListSettersRunner struct {
|
type ListSettersRunner struct {
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
Lookup setters.LookupSetters
|
Lookup setters.LookupSetters
|
||||||
List setters2.List
|
List setters2.List
|
||||||
Markdown bool
|
Markdown bool
|
||||||
IncludeSubst bool
|
IncludeSubst bool
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
|
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
@@ -63,28 +67,50 @@ func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
|
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
|
||||||
if setterVersion == "v2" {
|
if setterVersion == "v2" {
|
||||||
if err := r.ListSetters(c, args); err != nil {
|
e := executeCmdOnPkgs{
|
||||||
return err
|
needOpenAPI: true,
|
||||||
|
writer: c.OutOrStdout(),
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
}
|
}
|
||||||
if r.IncludeSubst {
|
|
||||||
return r.ListSubstitutions(c, args)
|
err := e.execute()
|
||||||
|
if err != nil {
|
||||||
|
return handleError(c, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleError(c, lookup(r.Lookup, c, args))
|
return handleError(c, lookup(r.Lookup, c, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ListSettersRunner) ListSetters(c *cobra.Command, args []string) error {
|
func (r *ListSettersRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
// use setters v2
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
path, err := ext.GetOpenAPIFile(args)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := r.List.ListSetters(path, args[0]); err != nil {
|
r.List = setters2.List{
|
||||||
|
Name: r.List.Name,
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
}
|
||||||
|
openAPIPath := filepath.Join(pkgPath, openAPIFileName)
|
||||||
|
if err := r.ListSetters(w, openAPIPath, pkgPath); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
table := newTable(c.OutOrStdout(), r.Markdown)
|
if r.IncludeSubst {
|
||||||
|
if err := r.ListSubstitutions(w, openAPIPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ListSettersRunner) ListSetters(w io.Writer, openAPIPath, resourcePath string) error {
|
||||||
|
// use setters v2
|
||||||
|
if err := r.List.ListSetters(openAPIPath, resourcePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
table := newTable(w, r.Markdown)
|
||||||
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED"})
|
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED"})
|
||||||
for i := range r.List.Setters {
|
for i := range r.List.Setters {
|
||||||
s := r.List.Setters[i]
|
s := r.List.Setters[i]
|
||||||
@@ -115,16 +141,12 @@ func (r *ListSettersRunner) ListSetters(c *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ListSettersRunner) ListSubstitutions(c *cobra.Command, args []string) error {
|
func (r *ListSettersRunner) ListSubstitutions(w io.Writer, openAPIPath string) error {
|
||||||
// use setters v2
|
// use setters v2
|
||||||
path, err := ext.GetOpenAPIFile(args)
|
if err := r.List.ListSubst(openAPIPath); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := r.List.ListSubst(path); err != nil {
|
table := newTable(w, r.Markdown)
|
||||||
return err
|
|
||||||
}
|
|
||||||
table := newTable(c.OutOrStdout(), r.Markdown)
|
|
||||||
b := tablewriter.Border{Top: true}
|
b := tablewriter.Border{Top: true}
|
||||||
table.SetBorders(b)
|
table.SetBorders(b)
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
@@ -408,27 +408,19 @@ openAPI:
|
|||||||
openapi.ResetOpenAPI()
|
openapi.ResetOpenAPI()
|
||||||
defer openapi.ResetOpenAPI()
|
defer openapi.ResetOpenAPI()
|
||||||
|
|
||||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
dir, err := ioutil.TempDir("", "")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer os.Remove(f.Name())
|
|
||||||
old := ext.GetOpenAPIFile
|
|
||||||
defer func() { ext.GetOpenAPIFile = old }()
|
|
||||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
|
||||||
err = ioutil.WriteFile(f.Name(), []byte(test.openapi), 0600)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "Krmfile"), []byte(test.openapi), 0600)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer os.Remove(r.Name())
|
|
||||||
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
err = ioutil.WriteFile(filepath.Join(dir, "deployment.yaml"), []byte(test.input), 0600)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -436,18 +428,18 @@ openAPI:
|
|||||||
runner := commands.NewListSettersRunner("")
|
runner := commands.NewListSettersRunner("")
|
||||||
actual := &bytes.Buffer{}
|
actual := &bytes.Buffer{}
|
||||||
runner.Command.SetOut(actual)
|
runner.Command.SetOut(actual)
|
||||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
runner.Command.SetArgs(append([]string{dir}, test.args...))
|
||||||
err = runner.Command.Execute()
|
err = runner.Command.Execute()
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, test.expected, actual.String()) {
|
if !assert.Contains(t, actual.String(), test.expected) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure that the resources are not altered
|
// make sure that the resources are not altered
|
||||||
actualResources, err := ioutil.ReadFile(r.Name())
|
actualResources, err := ioutil.ReadFile(filepath.Join(dir, "deployment.yaml"))
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -457,7 +449,7 @@ openAPI:
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
actualOpenAPI, err := ioutil.ReadFile(filepath.Join(dir, "Krmfile"))
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -469,3 +461,74 @@ openAPI:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListSettersSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "list-replicas",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
args: []string{"--include-subst"},
|
||||||
|
expected: `
|
||||||
|
|
||||||
|
test/testdata/dataset-with-setters/mysql/
|
||||||
|
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
||||||
|
image mysql 1 No
|
||||||
|
namespace myspace 1 No
|
||||||
|
tag 1.7.9 1 No
|
||||||
|
--------------- ----------------- --------------
|
||||||
|
SUBSTITUTION PATTERN REFERENCES
|
||||||
|
image-tag ${image}:${tag} [image,tag]
|
||||||
|
|
||||||
|
test/testdata/dataset-with-setters/mysql/nosetters/
|
||||||
|
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
||||||
|
|
||||||
|
test/testdata/dataset-with-setters/mysql/storage/
|
||||||
|
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
||||||
|
namespace myspace 1 No
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list-replicas",
|
||||||
|
dataset: "dataset-with-setters/mysql",
|
||||||
|
args: []string{"--recurse-subpackages=false"},
|
||||||
|
expected: `
|
||||||
|
|
||||||
|
test/testdata/dataset-with-setters/mysql/
|
||||||
|
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
||||||
|
image mysql 1 No
|
||||||
|
namespace myspace 1 No
|
||||||
|
tag 1.7.9 1 No
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
dir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
|
||||||
|
runner := commands.NewListSettersRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{dir}, test.args...))
|
||||||
|
err := runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(actual.String(), "\\", "/", -1)
|
||||||
|
|
||||||
|
if !assert.Equal(t, strings.TrimSpace(test.expected), strings.TrimSpace(actualNormalized)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -39,6 +41,8 @@ func NewSetRunner(parent string) *SetRunner {
|
|||||||
"annotate the field with a description of its value")
|
"annotate the field with a description of its value")
|
||||||
c.Flags().StringVar(&setterVersion, "version", "",
|
c.Flags().StringVar(&setterVersion, "version", "",
|
||||||
"use this version of the setter format")
|
"use this version of the setter format")
|
||||||
|
c.Flags().BoolVarP(&r.Set.RecurseSubPackages, "recurse-subpackages", "R", false,
|
||||||
|
"sets recursively in all the nested subpackages")
|
||||||
c.Flags().MarkHidden("version")
|
c.Flags().MarkHidden("version")
|
||||||
|
|
||||||
return r
|
return r
|
||||||
@@ -126,15 +130,23 @@ func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *SetRunner) runE(c *cobra.Command, args []string) error {
|
func (r *SetRunner) runE(c *cobra.Command, args []string) error {
|
||||||
if setterVersion == "v2" {
|
if setterVersion == "v2" {
|
||||||
count, err := r.Set.Set(r.OpenAPIFile, args[0])
|
e := executeCmdOnPkgs{
|
||||||
fmt.Fprintf(c.OutOrStdout(), "set %d fields\n", count)
|
needOpenAPI: true,
|
||||||
return handleError(c, err)
|
writer: c.OutOrStdout(),
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
recurseSubPackages: r.Set.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
}
|
||||||
|
err := e.execute()
|
||||||
|
if err != nil {
|
||||||
|
return handleError(c, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if len(args) > 2 || c.Flag("values").Changed {
|
if len(args) > 2 || c.Flag("values").Changed {
|
||||||
return handleError(c, r.perform(c, args))
|
return handleError(c, r.perform(c, args))
|
||||||
@@ -142,6 +154,38 @@ func (r *SetRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
return handleError(c, lookup(r.Lookup, c, args))
|
return handleError(c, lookup(r.Lookup, c, args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *SetRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Set = settersutil.FieldSetter{
|
||||||
|
Name: r.Set.Name,
|
||||||
|
Value: r.Set.Value,
|
||||||
|
ListValues: r.Set.ListValues,
|
||||||
|
Description: r.Set.Description,
|
||||||
|
SetBy: r.Set.SetBy,
|
||||||
|
Count: 0,
|
||||||
|
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
ResourcesPath: pkgPath,
|
||||||
|
RecurseSubPackages: r.Set.RecurseSubPackages,
|
||||||
|
}
|
||||||
|
count, err := r.Set.Set()
|
||||||
|
if err != nil {
|
||||||
|
// return err if RecurseSubPackages is false
|
||||||
|
if !r.Set.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if RecurseSubPackages is true
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "set %d field(s)\n", count)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func lookup(l setters.LookupSetters, c *cobra.Command, args []string) error {
|
func lookup(l setters.LookupSetters, c *cobra.Command, args []string) error {
|
||||||
// lookup the setters
|
// lookup the setters
|
||||||
err := kio.Pipeline{
|
err := kio.Pipeline{
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/ext"
|
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ func TestSetCommand(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "set replicas",
|
name: "set replicas",
|
||||||
args: []string{"replicas", "4", "--description", "hi there", "--set-by", "pw"},
|
args: []string{"replicas", "4", "--description", "hi there", "--set-by", "pw"},
|
||||||
out: "set 1 fields\n",
|
out: "set 1 field(s)\n",
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -126,7 +127,7 @@ spec:
|
|||||||
{
|
{
|
||||||
name: "set replicas no description",
|
name: "set replicas no description",
|
||||||
args: []string{"replicas", "4"},
|
args: []string{"replicas", "4"},
|
||||||
out: "set 1 fields\n",
|
out: "set 1 field(s)\n",
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -174,7 +175,7 @@ spec:
|
|||||||
{
|
{
|
||||||
name: "set image with value",
|
name: "set image with value",
|
||||||
args: []string{"tag", "1.8.1"},
|
args: []string{"tag", "1.8.1"},
|
||||||
out: "set 1 fields\n",
|
out: "set 1 field(s)\n",
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -509,7 +510,7 @@ list in body should have at most 2 items`,
|
|||||||
{
|
{
|
||||||
name: "set replicas with value set by flag",
|
name: "set replicas with value set by flag",
|
||||||
args: []string{"replicas", "--values", "4", "--description", "hi there"},
|
args: []string{"replicas", "--values", "4", "--description", "hi there"},
|
||||||
out: "set 1 fields\n",
|
out: "set 1 field(s)\n",
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -605,7 +606,7 @@ spec:
|
|||||||
{
|
{
|
||||||
name: "openAPI list values set by flag success",
|
name: "openAPI list values set by flag success",
|
||||||
args: []string{"list", "--values", "10", "--values", "11"},
|
args: []string{"list", "--values", "10", "--values", "11"},
|
||||||
out: "set 1 fields\n",
|
out: "set 1 field(s)\n",
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
kind: Kptfile
|
kind: Kptfile
|
||||||
openAPI:
|
openAPI:
|
||||||
@@ -709,7 +710,7 @@ list in body should have at most 2 items`,
|
|||||||
{
|
{
|
||||||
name: "nested substitution",
|
name: "nested substitution",
|
||||||
args: []string{"my-image-setter", "ubuntu"},
|
args: []string{"my-image-setter", "ubuntu"},
|
||||||
out: "set 2 fields\n",
|
out: "set 2 field(s)\n",
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
@@ -1013,22 +1014,19 @@ spec:
|
|||||||
openapi.ResetOpenAPI()
|
openapi.ResetOpenAPI()
|
||||||
defer openapi.ResetOpenAPI()
|
defer openapi.ResetOpenAPI()
|
||||||
|
|
||||||
f, err := ioutil.TempFile("", "k8s-cli-")
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer os.Remove(f.Name())
|
defer os.RemoveAll(baseDir)
|
||||||
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
|
||||||
|
f := filepath.Join(baseDir, "Krmfile")
|
||||||
|
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
old := ext.GetOpenAPIFile
|
|
||||||
defer func() { ext.GetOpenAPIFile = old }()
|
|
||||||
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
|
|
||||||
return f.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
|
r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -1041,7 +1039,7 @@ spec:
|
|||||||
runner := commands.NewSetRunner("")
|
runner := commands.NewSetRunner("")
|
||||||
out := &bytes.Buffer{}
|
out := &bytes.Buffer{}
|
||||||
runner.Command.SetOut(out)
|
runner.Command.SetOut(out)
|
||||||
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
runner.Command.SetArgs(append([]string{baseDir}, test.args...))
|
||||||
err = runner.Command.Execute()
|
err = runner.Command.Execute()
|
||||||
if test.errMsg != "" {
|
if test.errMsg != "" {
|
||||||
if !assert.NotNil(t, err) {
|
if !assert.NotNil(t, err) {
|
||||||
@@ -1056,7 +1054,7 @@ spec:
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
if test.errMsg == "" && !assert.Equal(t, test.out, out.String()) {
|
if test.errMsg == "" && !assert.Contains(t, out.String(), strings.TrimSpace(test.out)) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1070,7 +1068,7 @@ spec:
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
actualOpenAPI, err := ioutil.ReadFile(f)
|
||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -1082,3 +1080,82 @@ spec:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "set-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
args: []string{"namespace", "otherspace", "-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
set 1 field(s)
|
||||||
|
|
||||||
|
${baseDir}/mysql/nosetters/
|
||||||
|
setter "namespace" is not found
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
set 1 field(s)
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "set-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
args: []string{"namespace", "otherspace"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
set 1 field(s)
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "set-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"namespace", "otherspace"},
|
||||||
|
expected: `${baseDir}/mysql/storage/
|
||||||
|
set 1 field(s)
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.NewSetRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(
|
||||||
|
strings.Replace(expected, "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -178,6 +178,51 @@ items:
|
|||||||
outputOverride = `apiVersion: config.kubernetes.io/v1alpha1
|
outputOverride = `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
kind: ResourceList
|
kind: ResourceList
|
||||||
items:
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mysql-deployment
|
||||||
|
namespace: myspace
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:1.7.9
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nosetters-deployment
|
||||||
|
namespace: myspace
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml'
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nosetters
|
||||||
|
image: nosetters:1.7.7
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: storage-deployment
|
||||||
|
namespace: myspace
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: storage
|
||||||
|
image: storage:1.7.7
|
||||||
- apiVersion: apps/v1
|
- apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/sets"
|
"sigs.k8s.io/kustomize/kyaml/sets"
|
||||||
@@ -17,18 +19,18 @@ import (
|
|||||||
func GetCountRunner(name string) *CountRunner {
|
func GetCountRunner(name string) *CountRunner {
|
||||||
r := &CountRunner{}
|
r := &CountRunner{}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "count DIR...",
|
Use: "count [DIR]",
|
||||||
|
Args: cobra.MaximumNArgs(1),
|
||||||
Short: commands.CountShort,
|
Short: commands.CountShort,
|
||||||
Long: commands.CountLong,
|
Long: commands.CountLong,
|
||||||
Example: commands.CountExamples,
|
Example: commands.CountExamples,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
}
|
}
|
||||||
fixDocs(name, c)
|
fixDocs(name, c)
|
||||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
|
||||||
"also print resources from subpackages.")
|
|
||||||
c.Flags().BoolVar(&r.Kind, "kind", true,
|
c.Flags().BoolVar(&r.Kind, "kind", true,
|
||||||
"count resources by kind.")
|
"count resources by kind.")
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
|
||||||
|
"prints count of resources recursively in all the nested subpackages")
|
||||||
r.Command = c
|
r.Command = c
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -42,20 +44,56 @@ type CountRunner struct {
|
|||||||
IncludeSubpackages bool
|
IncludeSubpackages bool
|
||||||
Kind bool
|
Kind bool
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *CountRunner) runE(c *cobra.Command, args []string) error {
|
func (r *CountRunner) runE(c *cobra.Command, args []string) error {
|
||||||
var inputs []kio.Reader
|
if len(args) == 0 {
|
||||||
for _, a := range args {
|
input := &kio.ByteReader{Reader: c.InOrStdin()}
|
||||||
inputs = append(inputs, kio.LocalPackageReader{
|
|
||||||
PackagePath: a,
|
return handleError(c, kio.Pipeline{
|
||||||
IncludeSubpackages: r.IncludeSubpackages,
|
Inputs: []kio.Reader{input},
|
||||||
})
|
Outputs: r.out(c.OutOrStdout()),
|
||||||
}
|
}.Execute())
|
||||||
if len(inputs) == 0 {
|
|
||||||
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e := executeCmdOnPkgs{
|
||||||
|
writer: c.OutOrStdout(),
|
||||||
|
needOpenAPI: false,
|
||||||
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
rootPkgPath: args[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CountRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
input := kio.LocalPackageReader{PackagePath: pkgPath, PackageFileName: openAPIFileName}
|
||||||
|
|
||||||
|
err = kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{input},
|
||||||
|
Outputs: r.out(w),
|
||||||
|
}.Execute()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// return err if there is only package
|
||||||
|
if !r.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if there are multiple packages to annotate
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CountRunner) out(w io.Writer) []kio.Writer {
|
||||||
var out []kio.Writer
|
var out []kio.Writer
|
||||||
if r.Kind {
|
if r.Kind {
|
||||||
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
|
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
|
||||||
@@ -69,20 +107,15 @@ func (r *CountRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
order := k.List()
|
order := k.List()
|
||||||
sort.Strings(order)
|
sort.Strings(order)
|
||||||
for _, k := range order {
|
for _, k := range order {
|
||||||
fmt.Fprintf(c.OutOrStdout(), "%s: %d\n", k, count[k])
|
fmt.Fprintf(w, "%s: %d\n", k, count[k])
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
|
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
|
||||||
fmt.Fprintf(c.OutOrStdout(), "%d\n", len(nodes))
|
fmt.Fprintf(w, "%d\n", len(nodes))
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
return handleError(c, kio.Pipeline{
|
return out
|
||||||
Inputs: inputs,
|
|
||||||
Outputs: out,
|
|
||||||
}.Execute())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCountCommand_files(t *testing.T) {
|
func TestCountCommand_files(t *testing.T) {
|
||||||
@@ -67,7 +70,80 @@ spec:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, "Deployment: 2\nService: 1\n", b.String()) {
|
if !assert.Contains(t, b.String(), "Deployment: 2\nService: 1\n") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCountSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "count-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
Deployment: 1
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
Deployment: 1
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "count-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
args: []string{"-R=false"},
|
||||||
|
packagePath: "mysql",
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
Deployment: 1
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "count-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"-R=false"},
|
||||||
|
expected: `${baseDir}/mysql/storage/
|
||||||
|
Deployment: 1
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.GetCountRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ openAPI:
|
|||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
expectedStdOut: `
|
expectedStdOut: `
|
||||||
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
./
|
||||||
|
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
||||||
replicas 3 1 No
|
replicas 3 1 No
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
@@ -33,6 +37,8 @@ formatting substitution verbs {'%n': 'metadata.name', '%s': 'metadata.namespace'
|
|||||||
`if true, override existing filepath annotations.`)
|
`if true, override existing filepath annotations.`)
|
||||||
c.Flags().BoolVar(&r.UseSchema, "use-schema", false,
|
c.Flags().BoolVar(&r.UseSchema, "use-schema", false,
|
||||||
`if true, uses openapi resource schema to format resources.`)
|
`if true, uses openapi resource schema to format resources.`)
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
|
||||||
|
"formats resource files recursively in all the nested subpackages")
|
||||||
r.Command = c
|
r.Command = c
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -43,12 +49,13 @@ func FmtCommand(name string) *cobra.Command {
|
|||||||
|
|
||||||
// FmtRunner contains the run function
|
// FmtRunner contains the run function
|
||||||
type FmtRunner struct {
|
type FmtRunner struct {
|
||||||
Command *cobra.Command
|
Command *cobra.Command
|
||||||
FilenamePattern string
|
FilenamePattern string
|
||||||
SetFilenames bool
|
SetFilenames bool
|
||||||
KeepAnnotations bool
|
KeepAnnotations bool
|
||||||
Override bool
|
Override bool
|
||||||
UseSchema bool
|
UseSchema bool
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
|
func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
@@ -59,17 +66,6 @@ func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
|
func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
|
||||||
f := []kio.Filter{filters.FormatFilter{
|
|
||||||
UseSchema: r.UseSchema,
|
|
||||||
}}
|
|
||||||
|
|
||||||
// format with file names
|
|
||||||
if r.SetFilenames {
|
|
||||||
f = append(f, &filters.FileSetter{
|
|
||||||
FilenamePattern: r.FilenamePattern,
|
|
||||||
Override: r.Override,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// format stdin if there are no args
|
// format stdin if there are no args
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
@@ -79,20 +75,64 @@ func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
KeepReaderAnnotations: r.KeepAnnotations,
|
KeepReaderAnnotations: r.KeepAnnotations,
|
||||||
}
|
}
|
||||||
return handleError(c, kio.Pipeline{
|
return handleError(c, kio.Pipeline{
|
||||||
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute())
|
Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute())
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range args {
|
for _, rootPkgPath := range args {
|
||||||
path := args[i]
|
e := executeCmdOnPkgs{
|
||||||
rw := &kio.LocalPackageReadWriter{
|
writer: c.OutOrStdout(),
|
||||||
NoDeleteFiles: true,
|
needOpenAPI: false,
|
||||||
PackagePath: path,
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
KeepReaderAnnotations: r.KeepAnnotations}
|
cmdRunner: r,
|
||||||
err := kio.Pipeline{
|
rootPkgPath: rootPkgPath,
|
||||||
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute()
|
}
|
||||||
|
|
||||||
|
err := e.execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleError(c, err)
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *FmtRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rw := &kio.LocalPackageReadWriter{
|
||||||
|
NoDeleteFiles: true,
|
||||||
|
PackagePath: pkgPath,
|
||||||
|
KeepReaderAnnotations: r.KeepAnnotations, PackageFileName: openAPIFileName}
|
||||||
|
err = kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// return err if RecurseSubPackages is false
|
||||||
|
if !r.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if RecurseSubPackages is true
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(w, "formatted resource files in the package\n")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FmtRunner) fmtFilters() []kio.Filter {
|
||||||
|
fmtFilters := []kio.Filter{filters.FormatFilter{
|
||||||
|
UseSchema: r.UseSchema,
|
||||||
|
}}
|
||||||
|
|
||||||
|
// format with file names
|
||||||
|
if r.SetFilenames {
|
||||||
|
fmtFilters = append(fmtFilters, &filters.FileSetter{
|
||||||
|
FilenamePattern: r.FilenamePattern,
|
||||||
|
Override: r.Override,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return fmtFilters
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,12 +7,15 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml"
|
"sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
"sigs.k8s.io/kustomize/kyaml/testutil"
|
"sigs.k8s.io/kustomize/kyaml/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -163,3 +166,78 @@ func TestCmd_failFileContents(t *testing.T) {
|
|||||||
// expect an error
|
// expect an error
|
||||||
assert.EqualError(t, err, "yaml: line 1: did not find expected node content")
|
assert.EqualError(t, err, "yaml: line 1: did not find expected node content")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFmtSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "fmt-recurse-subpackages",
|
||||||
|
dataset: "dataset-with-setters",
|
||||||
|
args: []string{"-R"},
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
formatted resource files in the package
|
||||||
|
|
||||||
|
${baseDir}/mysql/nosetters/
|
||||||
|
formatted resource files in the package
|
||||||
|
|
||||||
|
${baseDir}/mysql/storage/
|
||||||
|
formatted resource files in the package
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fmt-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql",
|
||||||
|
expected: `${baseDir}/mysql/
|
||||||
|
formatted resource files in the package
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fmt-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
expected: `${baseDir}/mysql/storage/
|
||||||
|
formatted resource files in the package
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.GetFmtRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
@@ -18,22 +20,21 @@ import (
|
|||||||
func GetGrepRunner(name string) *GrepRunner {
|
func GetGrepRunner(name string) *GrepRunner {
|
||||||
r := &GrepRunner{}
|
r := &GrepRunner{}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "grep QUERY [DIR]...",
|
Use: "grep QUERY [DIR]",
|
||||||
Short: commands.GrepShort,
|
Short: commands.GrepShort,
|
||||||
Long: commands.GrepLong,
|
Long: commands.GrepLong,
|
||||||
Example: commands.GrepExamples,
|
Example: commands.GrepExamples,
|
||||||
PreRunE: r.preRunE,
|
PreRunE: r.preRunE,
|
||||||
RunE: r.runE,
|
RunE: r.runE,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MaximumNArgs(2),
|
||||||
}
|
}
|
||||||
fixDocs(name, c)
|
fixDocs(name, c)
|
||||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
|
||||||
"also print resources from subpackages.")
|
|
||||||
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true,
|
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true,
|
||||||
"annotate resources with their file origins.")
|
"annotate resources with their file origins.")
|
||||||
c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "", false,
|
c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "", false,
|
||||||
"Selected Resources are those not matching any of the specified patterns..")
|
"Selected Resources are those not matching any of the specified patterns..")
|
||||||
|
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
|
||||||
|
"also print resources recursively in all the nested subpackages")
|
||||||
r.Command = c
|
r.Command = c
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -44,11 +45,11 @@ func GrepCommand(name string) *cobra.Command {
|
|||||||
|
|
||||||
// GrepRunner contains the run function
|
// GrepRunner contains the run function
|
||||||
type GrepRunner struct {
|
type GrepRunner struct {
|
||||||
IncludeSubpackages bool
|
KeepAnnotations bool
|
||||||
KeepAnnotations bool
|
Command *cobra.Command
|
||||||
Command *cobra.Command
|
|
||||||
filters.GrepFilter
|
filters.GrepFilter
|
||||||
Format bool
|
Format bool
|
||||||
|
RecurseSubPackages bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error {
|
func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
@@ -101,25 +102,57 @@ func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *GrepRunner) runE(c *cobra.Command, args []string) error {
|
func (r *GrepRunner) runE(c *cobra.Command, args []string) error {
|
||||||
var filters = []kio.Filter{r.GrepFilter}
|
if len(args) == 1 {
|
||||||
|
input := &kio.ByteReader{Reader: c.InOrStdin()}
|
||||||
var inputs []kio.Reader
|
return handleError(c, kio.Pipeline{
|
||||||
for _, a := range args[1:] {
|
Inputs: []kio.Reader{input},
|
||||||
inputs = append(inputs, kio.LocalPackageReader{
|
Filters: []kio.Filter{r.GrepFilter},
|
||||||
PackagePath: a,
|
Outputs: []kio.Writer{kio.ByteWriter{
|
||||||
IncludeSubpackages: r.IncludeSubpackages,
|
Writer: c.OutOrStdout(),
|
||||||
})
|
KeepReaderAnnotations: r.KeepAnnotations,
|
||||||
}
|
}},
|
||||||
if len(inputs) == 0 {
|
}.Execute())
|
||||||
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return handleError(c, kio.Pipeline{
|
e := executeCmdOnPkgs{
|
||||||
Inputs: inputs,
|
writer: c.OutOrStdout(),
|
||||||
Filters: filters,
|
needOpenAPI: false,
|
||||||
|
recurseSubPackages: r.RecurseSubPackages,
|
||||||
|
cmdRunner: r,
|
||||||
|
rootPkgPath: args[1],
|
||||||
|
skipPkgPathPrint: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.execute()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *GrepRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
input := kio.LocalPackageReader{PackagePath: pkgPath, PackageFileName: openAPIFileName}
|
||||||
|
|
||||||
|
err = kio.Pipeline{
|
||||||
|
Inputs: []kio.Reader{input},
|
||||||
|
Filters: []kio.Filter{r.GrepFilter},
|
||||||
Outputs: []kio.Writer{kio.ByteWriter{
|
Outputs: []kio.Writer{kio.ByteWriter{
|
||||||
Writer: c.OutOrStdout(),
|
Writer: w,
|
||||||
KeepReaderAnnotations: r.KeepAnnotations,
|
KeepReaderAnnotations: r.KeepAnnotations,
|
||||||
}},
|
}},
|
||||||
}.Execute())
|
}.Execute()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// return err if there is only package
|
||||||
|
if !r.RecurseSubPackages {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
// print error message and continue if there are multiple packages to annotate
|
||||||
|
fmt.Fprintf(w, "%s\n", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "---")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/copyutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestGrepCommand_files verifies grep reads the files and filters them
|
// TestGrepCommand_files verifies grep reads the files and filters them
|
||||||
@@ -68,7 +70,7 @@ spec:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, `kind: Deployment
|
if !assert.Contains(t, b.String(), `kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: nginx2
|
app: nginx2
|
||||||
@@ -90,7 +92,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: nginx
|
app: nginx
|
||||||
`, b.String()) {
|
`) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +138,7 @@ spec:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !assert.Equal(t, `kind: Deployment
|
if !assert.Contains(t, b.String(), `kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: nginx2
|
app: nginx2
|
||||||
@@ -156,7 +158,7 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: nginx
|
app: nginx
|
||||||
`, b.String()) {
|
`) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,7 +252,7 @@ spec:
|
|||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !assert.Equal(t, `kind: Deployment
|
if !assert.Contains(t, b.String(), `kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: nginx1.7
|
app: nginx1.7
|
||||||
@@ -264,7 +266,142 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: nginx:1.7.9
|
image: nginx:1.7.9
|
||||||
`, b.String()) {
|
`) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGrepSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
packagePath string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "grep-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
args: []string{"kind=Deployment"},
|
||||||
|
expected: `# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace
|
||||||
|
name: mysql-deployment
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'deployment.yaml'
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:1.7.9
|
||||||
|
---
|
||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace
|
||||||
|
name: storage-deployment
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'deployment.yaml'
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: storage
|
||||||
|
image: storage:1.7.7
|
||||||
|
---`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grep-top-level-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
args: []string{"kind=Deployment", "-R=false"},
|
||||||
|
packagePath: "mysql",
|
||||||
|
expected: `# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace
|
||||||
|
name: mysql-deployment
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'deployment.yaml'
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:1.7.9
|
||||||
|
---`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grep-nested-pkg-no-recurse-subpackages",
|
||||||
|
dataset: "dataset-without-setters",
|
||||||
|
packagePath: "mysql/storage",
|
||||||
|
args: []string{"kind=Deployment", "-R=false"},
|
||||||
|
expected: `# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace
|
||||||
|
name: storage-deployment
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/index: '0'
|
||||||
|
config.kubernetes.io/path: 'deployment.yaml'
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: storage
|
||||||
|
image: storage:1.7.7
|
||||||
|
---`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
sourceDir := filepath.Join("test", "testdata", test.dataset)
|
||||||
|
baseDir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
copyutil.CopyDir(sourceDir, baseDir)
|
||||||
|
defer os.RemoveAll(baseDir)
|
||||||
|
runner := commands.GetGrepRunner("")
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(actual)
|
||||||
|
runner.Command.SetArgs(append(test.args, filepath.Join(baseDir, test.packagePath)))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
|
||||||
|
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,11 +9,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCatRunner returns a RunFnRunner.
|
// GetCatRunner returns a RunFnRunner.
|
||||||
@@ -65,6 +67,11 @@ func GetRunFnRunner(name string) *RunFnRunner {
|
|||||||
r.Command.Flags().StringArrayVar(
|
r.Command.Flags().StringArrayVar(
|
||||||
&r.Mounts, "mount", []string{},
|
&r.Mounts, "mount", []string{},
|
||||||
"a list of storage options read from the filesystem")
|
"a list of storage options read from the filesystem")
|
||||||
|
r.Command.Flags().BoolVar(
|
||||||
|
&r.LogSteps, "log-steps", false, "log steps to stderr")
|
||||||
|
r.Command.Flags().StringArrayVarP(
|
||||||
|
&r.Env, "env", "e", []string{},
|
||||||
|
"a list of environment variables to be used by functions")
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +98,8 @@ type RunFnRunner struct {
|
|||||||
Network bool
|
Network bool
|
||||||
NetworkName string
|
NetworkName string
|
||||||
Mounts []string
|
Mounts []string
|
||||||
|
LogSteps bool
|
||||||
|
Env []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
||||||
@@ -99,7 +108,7 @@ func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
// getContainerFunctions parses the commandline flags and arguments into explicit
|
// getContainerFunctions parses the commandline flags and arguments into explicit
|
||||||
// Functions to run.
|
// Functions to run.
|
||||||
func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []string) (
|
func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, dataItems []string) (
|
||||||
[]*yaml.RNode, error) {
|
[]*yaml.RNode, error) {
|
||||||
|
|
||||||
if r.Image == "" && r.StarPath == "" && r.ExecPath == "" && r.StarURL == "" {
|
if r.Image == "" && r.StarPath == "" && r.ExecPath == "" && r.StarURL == "" {
|
||||||
@@ -193,7 +202,7 @@ data: {}
|
|||||||
}
|
}
|
||||||
err = rc.PipeE(
|
err = rc.PipeE(
|
||||||
yaml.LookupCreate(yaml.MappingNode, "metadata", "annotations"),
|
yaml.LookupCreate(yaml.MappingNode, "metadata", "annotations"),
|
||||||
yaml.SetField("config.kubernetes.io/function", yaml.NewScalarRNode(value)))
|
yaml.SetField(runtimeutil.FunctionAnnotationKey, yaml.NewScalarRNode(value)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -267,7 +276,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
return errors.Errorf("0 or 1 arguments supported, function arguments go after '--'")
|
return errors.Errorf("0 or 1 arguments supported, function arguments go after '--'")
|
||||||
}
|
}
|
||||||
|
|
||||||
fns, err := r.getContainerFunctions(c, args, dataItems)
|
fns, err := r.getContainerFunctions(c, dataItems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -305,6 +314,8 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
|||||||
EnableExec: r.EnableExec,
|
EnableExec: r.EnableExec,
|
||||||
StorageMounts: storageMounts,
|
StorageMounts: storageMounts,
|
||||||
ResultsDir: r.ResultsDir,
|
ResultsDir: r.ResultsDir,
|
||||||
|
LogSteps: r.LogSteps,
|
||||||
|
Env: r.Env,
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't consider args for the function
|
// don't consider args for the function
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -203,6 +204,7 @@ apiVersion: v1
|
|||||||
Path: "dir",
|
Path: "dir",
|
||||||
NetworkName: "bridge",
|
NetworkName: "bridge",
|
||||||
EnableStarlark: true,
|
EnableStarlark: true,
|
||||||
|
Env: []string{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -256,6 +258,7 @@ apiVersion: v1
|
|||||||
Path: "dir",
|
Path: "dir",
|
||||||
NetworkName: "bridge",
|
NetworkName: "bridge",
|
||||||
ResultsDir: "foo/",
|
ResultsDir: "foo/",
|
||||||
|
Env: []string{},
|
||||||
},
|
},
|
||||||
expected: `
|
expected: `
|
||||||
metadata:
|
metadata:
|
||||||
@@ -283,6 +286,27 @@ apiVersion: v1
|
|||||||
args: []string{"run", "dir", "--image", "foo:bar", "--", "a=b", "c", "e=f"},
|
args: []string{"run", "dir", "--image", "foo:bar", "--", "a=b", "c", "e=f"},
|
||||||
err: "must have keys and values separated by",
|
err: "must have keys and values separated by",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "log steps",
|
||||||
|
args: []string{"run", "dir", "--log-steps"},
|
||||||
|
path: "dir",
|
||||||
|
expectedStruct: &runfn.RunFns{
|
||||||
|
Path: "dir",
|
||||||
|
NetworkName: "bridge",
|
||||||
|
LogSteps: true,
|
||||||
|
Env: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "envs",
|
||||||
|
args: []string{"run", "dir", "--env", "FOO=BAR", "-e", "BAR"},
|
||||||
|
path: "dir",
|
||||||
|
expectedStruct: &runfn.RunFns{
|
||||||
|
Path: "dir",
|
||||||
|
NetworkName: "bridge",
|
||||||
|
Env: []string{"FOO=BAR", "BAR"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range tests {
|
for i := range tests {
|
||||||
@@ -385,5 +409,4 @@ apiVersion: v1
|
|||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/Krmfile
vendored
Normal file
33
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/Krmfile
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
apiVersion: krm.dev/v1alpha1
|
||||||
|
kind: Krmfile
|
||||||
|
metadata:
|
||||||
|
name: mysql
|
||||||
|
packageMetadata:
|
||||||
|
shortDescription: sample description
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.namespace:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: namespace
|
||||||
|
value: myspace
|
||||||
|
io.k8s.cli.substitutions.image-tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: image-tag
|
||||||
|
pattern: ${image}:${tag}
|
||||||
|
values:
|
||||||
|
- marker: ${image}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.image'
|
||||||
|
- marker: ${tag}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.tag'
|
||||||
|
io.k8s.cli.setters.image:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: image
|
||||||
|
value: mysql
|
||||||
|
io.k8s.cli.setters.tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: tag
|
||||||
|
value: 1.7.9
|
||||||
15
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/deployment.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace # {"$openapi":"namespace"}
|
||||||
|
name: mysql-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:1.7.9 # {"$openapi":"image-tag"}
|
||||||
6
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/nosetters/Krmfile
vendored
Normal file
6
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/nosetters/Krmfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: krm.dev/v1alpha1
|
||||||
|
kind: Krmfile
|
||||||
|
metadata:
|
||||||
|
name: storage
|
||||||
|
packageMetadata:
|
||||||
|
shortDescription: sample description
|
||||||
15
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/nosetters/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/nosetters/deployment.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace
|
||||||
|
name: nosetters-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nosetters
|
||||||
|
image: nosetters:1.7.7
|
||||||
13
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/storage/Krmfile
vendored
Normal file
13
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/storage/Krmfile
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
apiVersion: krm.dev/v1alpha1
|
||||||
|
kind: Krmfile
|
||||||
|
metadata:
|
||||||
|
name: storage
|
||||||
|
packageMetadata:
|
||||||
|
shortDescription: sample description
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.namespace:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: namespace
|
||||||
|
value: myspace
|
||||||
15
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/storage/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset-with-setters/mysql/storage/deployment.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace # {"$openapi":"namespace"}
|
||||||
|
name: storage-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: storage
|
||||||
|
image: storage:1.7.7
|
||||||
6
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/Krmfile
vendored
Normal file
6
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/Krmfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: krm.dev/v1alpha1
|
||||||
|
kind: Krmfile
|
||||||
|
metadata:
|
||||||
|
name: mysql
|
||||||
|
packageMetadata:
|
||||||
|
shortDescription: sample description
|
||||||
15
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/deployment.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace
|
||||||
|
name: mysql-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mysql
|
||||||
|
image: mysql:1.7.9
|
||||||
6
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/storage/Krmfile
vendored
Normal file
6
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/storage/Krmfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: krm.dev/v1alpha1
|
||||||
|
kind: Krmfile
|
||||||
|
metadata:
|
||||||
|
name: storage
|
||||||
|
packageMetadata:
|
||||||
|
shortDescription: sample description
|
||||||
15
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/storage/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset-without-setters/mysql/storage/deployment.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright 2019 The Kubernetes Authors.
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
namespace: myspace
|
||||||
|
name: storage-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 4
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: storage
|
||||||
|
image: storage:1.7.7
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||||
|
|
||||||
@@ -26,8 +27,6 @@ func GetTreeRunner(name string) *TreeRunner {
|
|||||||
Args: cobra.MaximumNArgs(1),
|
Args: cobra.MaximumNArgs(1),
|
||||||
}
|
}
|
||||||
fixDocs(name, c)
|
fixDocs(name, c)
|
||||||
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
|
|
||||||
"also print resources from subpackages.")
|
|
||||||
|
|
||||||
// TODO(pwittrock): Figure out if these are the right things to expose, and consider making it
|
// TODO(pwittrock): Figure out if these are the right things to expose, and consider making it
|
||||||
// a list of options instead of individual flags
|
// a list of options instead of individual flags
|
||||||
@@ -59,29 +58,33 @@ func TreeCommand(name string) *cobra.Command {
|
|||||||
|
|
||||||
// TreeRunner contains the run function
|
// TreeRunner contains the run function
|
||||||
type TreeRunner struct {
|
type TreeRunner struct {
|
||||||
IncludeSubpackages bool
|
Command *cobra.Command
|
||||||
Command *cobra.Command
|
name bool
|
||||||
name bool
|
resources bool
|
||||||
resources bool
|
ports bool
|
||||||
ports bool
|
images bool
|
||||||
images bool
|
replicas bool
|
||||||
replicas bool
|
all bool
|
||||||
all bool
|
env bool
|
||||||
env bool
|
args bool
|
||||||
args bool
|
cmd bool
|
||||||
cmd bool
|
fields []string
|
||||||
fields []string
|
includeLocal bool
|
||||||
includeLocal bool
|
excludeNonLocal bool
|
||||||
excludeNonLocal bool
|
structure string
|
||||||
structure string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
|
func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
|
||||||
var input kio.Reader
|
var input kio.Reader
|
||||||
var root = "."
|
var root = "."
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
matchFilesGlob := append([]string{openAPIFileName}, kio.DefaultMatch...)
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
root = filepath.Clean(args[0])
|
root = filepath.Clean(args[0])
|
||||||
input = kio.LocalPackageReader{PackagePath: args[0]}
|
input = kio.LocalPackageReader{PackagePath: args[0], MatchFilesGlob: matchFilesGlob}
|
||||||
} else {
|
} else {
|
||||||
input = &kio.ByteReader{Reader: c.InOrStdin()}
|
input = &kio.ByteReader{Reader: c.InOrStdin()}
|
||||||
}
|
}
|
||||||
@@ -156,10 +159,12 @@ func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
|
|||||||
Inputs: []kio.Reader{input},
|
Inputs: []kio.Reader{input},
|
||||||
Filters: fltrs,
|
Filters: fltrs,
|
||||||
Outputs: []kio.Writer{kio.TreeWriter{
|
Outputs: []kio.Writer{kio.TreeWriter{
|
||||||
Root: root,
|
Root: root,
|
||||||
Writer: c.OutOrStdout(),
|
Writer: c.OutOrStdout(),
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
Structure: kio.TreeStructure(r.structure)}},
|
Structure: kio.TreeStructure(r.structure),
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
}},
|
||||||
}.Execute())
|
}.Execute())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,109 @@ spec:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTreeCommand_subpkgs(t *testing.T) {
|
||||||
|
d, err := ioutil.TempDir("", "kustomize-tree-test")
|
||||||
|
defer os.RemoveAll(d)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.MkdirAll(filepath.Join(d, "subpkg"), 0700)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Abstraction
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
configFn:
|
||||||
|
container:
|
||||||
|
image: gcr.io/example/reconciler:v1
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/local-config: "true"
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
---
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx2
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
app: nginx2
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
---
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
`), 0600)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(d, "subpkg", "f2.yaml"), []byte(`kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: bar
|
||||||
|
annotations:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
`), 0600)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(d, "Krmfile"), []byte(`apiVersion: kpt.dev/v1alpha1
|
||||||
|
kind: Krmfile
|
||||||
|
metadata:
|
||||||
|
name: mainpkg
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
`), 0600)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(d, "subpkg", "Krmfile"), []byte(`apiVersion: kpt.dev/v1alpha1
|
||||||
|
kind: Krmfile
|
||||||
|
metadata:
|
||||||
|
name: subpkg
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
`), 0600)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fmt the files
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
r := commands.GetTreeRunner("")
|
||||||
|
r.Command.SetArgs([]string{d})
|
||||||
|
r.Command.SetOut(b)
|
||||||
|
if !assert.NoError(t, r.Command.Execute()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.Equal(t, fmt.Sprintf(`%s
|
||||||
|
├── [Krmfile] Krmfile mainpkg
|
||||||
|
├── [f1.yaml] Deployment foo
|
||||||
|
├── [f1.yaml] Service foo
|
||||||
|
└── Pkg: subpkg
|
||||||
|
├── [Krmfile] Krmfile subpkg
|
||||||
|
└── [f2.yaml] Deployment bar
|
||||||
|
`, d), b.String()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTreeCommand_stdin(t *testing.T) {
|
func TestTreeCommand_stdin(t *testing.T) {
|
||||||
// fmt the files
|
// fmt the files
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
|
|||||||
@@ -5,13 +5,91 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/pathutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// cmdRunner interface holds executeCmd definition which executes respective command's
|
||||||
|
// implementation on single package
|
||||||
|
type cmdRunner interface {
|
||||||
|
executeCmd(w io.Writer, pkgPath string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeCmdOnPkgs struct holds the parameters necessary to
|
||||||
|
// execute the filter command on packages in rootPkgPath
|
||||||
|
type executeCmdOnPkgs struct {
|
||||||
|
rootPkgPath string
|
||||||
|
recurseSubPackages bool
|
||||||
|
needOpenAPI bool
|
||||||
|
cmdRunner cmdRunner
|
||||||
|
writer io.Writer
|
||||||
|
skipPkgPathPrint bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeCmdOnPkgs takes the function definition for a command to be executed on single package, applies that definition
|
||||||
|
// recursively on all the subpackages present in rootPkgPath if recurseSubPackages is true, else applies the command on rootPkgPath only
|
||||||
|
func (e executeCmdOnPkgs) execute() error {
|
||||||
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkgsPaths, err := pathutil.DirsWithFile(e.rootPkgPath, openAPIFileName, e.recurseSubPackages)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pkgsPaths) == 0 {
|
||||||
|
// at this point, there are no openAPI files in the rootPkgPath
|
||||||
|
if e.needOpenAPI {
|
||||||
|
// few executions need openAPI file to be present(ex: setters commands), if true throw an error
|
||||||
|
return errors.Errorf("unable to find %q in package %q", openAPIFileName, e.rootPkgPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add root path for commands which doesn't need openAPI(ex: annotate, fmt)
|
||||||
|
pkgsPaths = []string{e.rootPkgPath}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range pkgsPaths {
|
||||||
|
pkgPath := pkgsPaths[i]
|
||||||
|
// Add schema present in openAPI file for current package
|
||||||
|
if e.needOpenAPI {
|
||||||
|
if err := openapi.AddSchemaFromFile(filepath.Join(pkgPath, openAPIFileName)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !e.skipPkgPathPrint {
|
||||||
|
fmt.Fprintf(e.writer, "%s/\n", pkgPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.cmdRunner.executeCmd(e.writer, pkgPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if i != len(pkgsPaths)-1 {
|
||||||
|
fmt.Fprint(e.writer, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete schema present in openAPI file for current package
|
||||||
|
if e.needOpenAPI {
|
||||||
|
if err := openapi.DeleteSchemaInFile(filepath.Join(pkgPath, openAPIFileName)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// parseFieldPath parse a flag value into a field path
|
// parseFieldPath parse a flag value into a field path
|
||||||
func parseFieldPath(path string) ([]string, error) {
|
func parseFieldPath(path string) ([]string, error) {
|
||||||
// fixup '\.' so we don't split on it
|
// fixup '\.' so we don't split on it
|
||||||
|
|||||||
184
cmd/config/internal/commands/util_test.go
Normal file
184
cmd/config/internal/commands/util_test.go
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExecuteCmdOnPkgs(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
recurse bool
|
||||||
|
pkgPath string
|
||||||
|
needOpenAPI bool
|
||||||
|
errMsg string
|
||||||
|
expectedOut string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "need_Krmfile_error",
|
||||||
|
recurse: true,
|
||||||
|
needOpenAPI: true,
|
||||||
|
pkgPath: "subpkg1/subdir1",
|
||||||
|
errMsg: `unable to find "Krmfile" in package`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Krmfile_not_needed_no_err",
|
||||||
|
recurse: true,
|
||||||
|
needOpenAPI: false,
|
||||||
|
pkgPath: "subpkg1/subdir1",
|
||||||
|
expectedOut: `${baseDir}/subpkg1/subdir1/
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "executeCmd_returns_error",
|
||||||
|
recurse: true,
|
||||||
|
needOpenAPI: false,
|
||||||
|
pkgPath: "subpkg4",
|
||||||
|
expectedOut: `${baseDir}/subpkg4/
|
||||||
|
`,
|
||||||
|
errMsg: `this command returns an error if package has error.txt file`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "executeCmd_prints_pkgpaths",
|
||||||
|
recurse: true,
|
||||||
|
needOpenAPI: false,
|
||||||
|
pkgPath: "subpkg2",
|
||||||
|
expectedOut: `${baseDir}/subpkg2/
|
||||||
|
|
||||||
|
${baseDir}/subpkg2/subpkg3/
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
err = createTestDirStructure(dir)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
actual := &bytes.Buffer{}
|
||||||
|
r := &TestRunner{}
|
||||||
|
e := executeCmdOnPkgs{
|
||||||
|
needOpenAPI: test.needOpenAPI,
|
||||||
|
writer: actual,
|
||||||
|
rootPkgPath: filepath.Join(dir, test.pkgPath),
|
||||||
|
recurseSubPackages: test.recurse,
|
||||||
|
cmdRunner: r,
|
||||||
|
}
|
||||||
|
err := e.execute()
|
||||||
|
if test.errMsg == "" {
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !assert.Error(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Contains(t, err.Error(), test.errMsg) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize path format for windows
|
||||||
|
actualNormalized := strings.Replace(
|
||||||
|
strings.Replace(actual.String(), "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
|
||||||
|
expected := strings.Replace(test.expectedOut, "${baseDir}", dir+"/", -1)
|
||||||
|
expectedNormalized := strings.Replace(
|
||||||
|
strings.Replace(expected, "\\", "/", -1),
|
||||||
|
"//", "/", -1)
|
||||||
|
if !assert.Equal(t, expectedNormalized, actualNormalized) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTestDirStructure(dir string) error {
|
||||||
|
/*
|
||||||
|
Adds the folders to the input dir with following structure
|
||||||
|
dir
|
||||||
|
├── subpkg1
|
||||||
|
│ ├── Krmfile
|
||||||
|
│ └── subdir1
|
||||||
|
├── subpkg4
|
||||||
|
│ ├── Krmfile
|
||||||
|
│ └── error.txt
|
||||||
|
└── subpkg2
|
||||||
|
├── subpkg3
|
||||||
|
│ ├── Krmfile
|
||||||
|
│ └── subdir2
|
||||||
|
└── Krmfile
|
||||||
|
*/
|
||||||
|
err := os.MkdirAll(filepath.Join(dir, "subpkg1/subdir1"), 0777|os.ModeDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(filepath.Join(dir, "subpkg2/subpkg3/subdir2"), 0777|os.ModeDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(filepath.Join(dir, "subpkg4"), 0777|os.ModeDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "subpkg1", "Krmfile"), []byte(""), 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "subpkg2", "Krmfile"), []byte(""), 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "subpkg2/subpkg3", "Krmfile"), []byte(""), 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "subpkg4", "error.txt"), []byte(""), 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "subpkg4", "Krmfile"), []byte(""), 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "Krmfile"), []byte(""), 0777)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestRunner struct{}
|
||||||
|
|
||||||
|
func (r *TestRunner) executeCmd(w io.Writer, pkgPath string) error {
|
||||||
|
children, err := ioutil.ReadDir(pkgPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, child := range children {
|
||||||
|
if child.Name() == "error.txt" {
|
||||||
|
return errors.Errorf("this command returns an error if package has error.txt file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="Glossary">
|
<meta itemprop="name" content="Glossary">
|
||||||
<meta itemprop="description" content="Glossary of terms
|
<meta itemprop="description" content="Glossary of terms
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="Glossary"/>
|
<meta name="twitter:title" content="Glossary"/>
|
||||||
<meta name="twitter:description" content="Glossary of terms
|
<meta name="twitter:description" content="Glossary of terms
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="API Reference">
|
<meta itemprop="name" content="API Reference">
|
||||||
<meta itemprop="description" content="Reference for Kustomize client-side APIs
|
<meta itemprop="description" content="Reference for Kustomize client-side APIs
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="API Reference"/>
|
<meta name="twitter:title" content="API Reference"/>
|
||||||
<meta name="twitter:description" content="Reference for Kustomize client-side APIs
|
<meta name="twitter:description" content="Reference for Kustomize client-side APIs
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="bases">
|
<meta itemprop="name" content="bases">
|
||||||
<meta itemprop="description" content="Add resources from a kustomization dir.
|
<meta itemprop="description" content="Add resources from a kustomization dir.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="bases"/>
|
<meta name="twitter:title" content="bases"/>
|
||||||
<meta name="twitter:description" content="Add resources from a kustomization dir.
|
<meta name="twitter:description" content="Add resources from a kustomization dir.
|
||||||
"/>
|
"/>
|
||||||
@@ -755,7 +756,7 @@
|
|||||||
|
|
||||||
<p>Move entries into the <a href="/kustomize/api-reference/kustomization/resources">resources</a>
|
<p>Move entries into the <a href="/kustomize/api-reference/kustomization/resources">resources</a>
|
||||||
field. This allows bases - which are still a
|
field. This allows bases - which are still a
|
||||||
<a href="/kustomize/api-reference/kustomization/glossary#base">central concept</a> - to be
|
<a href="/kustomize/api-reference/glossary#base">central concept</a> - to be
|
||||||
ordered relative to other input resources.</p>
|
ordered relative to other input resources.</p>
|
||||||
|
|
||||||
<div class="section-index">
|
<div class="section-index">
|
||||||
@@ -812,7 +813,7 @@ ordered relative to other input resources.</p>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="text-muted mt-5 pt-3 border-top">Last modified July 16, 2020: <a href="https://github.com/kubernetes-sigs/kustomize/commit/f9ee578aed600136133c3232fff03029cdfc526e">Docs: Auto-fix markdownlint issues (f9ee578a)</a>
|
<div class="text-muted mt-5 pt-3 border-top">Last modified August 20, 2020: <a href="https://github.com/kubernetes-sigs/kustomize/commit/b450b624e8ff259bc52ce85d439b7f58dd548f27">Update dead URL to base in glossary. (b450b624)</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="commonAnnotations">
|
<meta itemprop="name" content="commonAnnotations">
|
||||||
<meta itemprop="description" content="Add annotations to add all resources.
|
<meta itemprop="description" content="Add annotations to add all resources.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="commonAnnotations"/>
|
<meta name="twitter:title" content="commonAnnotations"/>
|
||||||
<meta name="twitter:description" content="Add annotations to add all resources.
|
<meta name="twitter:description" content="Add annotations to add all resources.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="commonLabels">
|
<meta itemprop="name" content="commonLabels">
|
||||||
<meta itemprop="description" content="Add labels and selectors to add all resources.
|
<meta itemprop="description" content="Add labels and selectors to add all resources.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="commonLabels"/>
|
<meta name="twitter:title" content="commonLabels"/>
|
||||||
<meta name="twitter:description" content="Add labels and selectors to add all resources.
|
<meta name="twitter:description" content="Add labels and selectors to add all resources.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="components">
|
<meta itemprop="name" content="components">
|
||||||
<meta itemprop="description" content="Compose kustomizations.
|
<meta itemprop="description" content="Compose kustomizations.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="components"/>
|
<meta name="twitter:title" content="components"/>
|
||||||
<meta name="twitter:description" content="Compose kustomizations.
|
<meta name="twitter:description" content="Compose kustomizations.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="configMapGenerator">
|
<meta itemprop="name" content="configMapGenerator">
|
||||||
<meta itemprop="description" content="Generate ConfigMap resources.
|
<meta itemprop="description" content="Generate ConfigMap resources.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="configMapGenerator"/>
|
<meta name="twitter:title" content="configMapGenerator"/>
|
||||||
<meta name="twitter:description" content="Generate ConfigMap resources.
|
<meta name="twitter:description" content="Generate ConfigMap resources.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="crds">
|
<meta itemprop="name" content="crds">
|
||||||
<meta itemprop="description" content="Adding CRD support
|
<meta itemprop="description" content="Adding CRD support
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="crds"/>
|
<meta name="twitter:title" content="crds"/>
|
||||||
<meta name="twitter:description" content="Adding CRD support
|
<meta name="twitter:description" content="Adding CRD support
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="generatorOptions">
|
<meta itemprop="name" content="generatorOptions">
|
||||||
<meta itemprop="description" content="Control behavior of [ConfigMap](/kustomize/api-reference/kustomization/configmapgenerator) and [Secret](/kustomize/api-reference/kustomization/secretgenerator) generators.
|
<meta itemprop="description" content="Control behavior of [ConfigMap](/kustomize/api-reference/kustomization/configmapgenerator) and [Secret](/kustomize/api-reference/kustomization/secretgenerator) generators.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="generatorOptions"/>
|
<meta name="twitter:title" content="generatorOptions"/>
|
||||||
<meta name="twitter:description" content="Control behavior of [ConfigMap](/kustomize/api-reference/kustomization/configmapgenerator) and [Secret](/kustomize/api-reference/kustomization/secretgenerator) generators.
|
<meta name="twitter:description" content="Control behavior of [ConfigMap](/kustomize/api-reference/kustomization/configmapgenerator) and [Secret](/kustomize/api-reference/kustomization/secretgenerator) generators.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="images">
|
<meta itemprop="name" content="images">
|
||||||
<meta itemprop="description" content="Modify the name, tags and/or digest for images.
|
<meta itemprop="description" content="Modify the name, tags and/or digest for images.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="images"/>
|
<meta name="twitter:title" content="images"/>
|
||||||
<meta name="twitter:description" content="Modify the name, tags and/or digest for images.
|
<meta name="twitter:description" content="Modify the name, tags and/or digest for images.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="kustomization.yaml">
|
<meta itemprop="name" content="kustomization.yaml">
|
||||||
<meta itemprop="description" content="kustomization.yaml fields and API
|
<meta itemprop="description" content="kustomization.yaml fields and API
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="kustomization.yaml"/>
|
<meta name="twitter:title" content="kustomization.yaml"/>
|
||||||
<meta name="twitter:description" content="kustomization.yaml fields and API
|
<meta name="twitter:description" content="kustomization.yaml fields and API
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<p>Move entries into the <a href="https://kubernetes-sigs.github.io/kustomize/kustomize/api-reference/kustomization/resources">resources</a>
|
<p>Move entries into the <a href="https://kubernetes-sigs.github.io/kustomize/kustomize/api-reference/kustomization/resources">resources</a>
|
||||||
field. This allows bases - which are still a
|
field. This allows bases - which are still a
|
||||||
<a href="https://kubernetes-sigs.github.io/kustomize/kustomize/api-reference/kustomization/glossary#base">central concept</a> - to be
|
<a href="https://kubernetes-sigs.github.io/kustomize/kustomize/api-reference/glossary#base">central concept</a> - to be
|
||||||
ordered relative to other input resources.</p>
|
ordered relative to other input resources.</p>
|
||||||
|
|
||||||
</description>
|
</description>
|
||||||
@@ -492,7 +492,80 @@ to it (regular expressions).</p>
|
|||||||
</span></code></pre></div><p>The <code>name</code> and <code>namespace</code> fields of the patch target selector are
|
</span></code></pre></div><p>The <code>name</code> and <code>namespace</code> fields of the patch target selector are
|
||||||
automatically anchored regular expressions. This means that the value <code>myapp</code>
|
automatically anchored regular expressions. This means that the value <code>myapp</code>
|
||||||
is equivalent to <code>^myapp$</code>.</p>
|
is equivalent to <code>^myapp$</code>.</p>
|
||||||
|
<p>Consider the following <code>deployment.yaml</code> common for both the examples:</p>
|
||||||
|
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#204a87;font-weight:bold">apiVersion</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>apps/v1<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>Deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">metadata</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">replicas</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">template</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">containers</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-container<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>registry/conatiner<span style="color:#000;font-weight:bold">:</span>latest<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span></code></pre></div><h2 id="example-i">Example I</h2>
|
||||||
|
<h3 id="intent">Intent</h3>
|
||||||
|
<p>To Make the container image point to a specific version and not to the latest container in the
|
||||||
|
registry.</p>
|
||||||
|
<h3 id="file-input">File Input</h3>
|
||||||
|
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#8f5902;font-style:italic"># kustomization.yaml</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">resources</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span>- deployment.yaml<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">patches</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span>- <span style="color:#204a87;font-weight:bold">path</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>patch.yaml<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span></code></pre></div><div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#8f5902;font-style:italic"># patch.yaml</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">apiVersion</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>apps/v1<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>Deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">metadata</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">template</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">containers</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-container<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>registry/conatiner<span style="color:#000;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">1.0.0</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span></code></pre></div><h3 id="build-output">Build Output</h3>
|
||||||
|
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#204a87;font-weight:bold">apiVersion</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>apps/v1<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>Deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">metadata</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">replicas</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">template</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">containers</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>registry/conatiner<span style="color:#000;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">1.0.0</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-container<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span></code></pre></div><h2 id="example-ii">Example II</h2>
|
||||||
|
<h3 id="intent-1">Intent</h3>
|
||||||
|
<p>To Make the container image point to a specific version and not to the latest container in the
|
||||||
|
registry.</p>
|
||||||
|
<h3 id="file-input-1">File Input</h3>
|
||||||
|
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#8f5902;font-style:italic"># kustomization.yaml</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">resources</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span>- deployment.yaml<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">patches</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span>- <span style="color:#204a87;font-weight:bold">target</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>Deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">path</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>patch.json<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span></code></pre></div><div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#8f5902;font-style:italic"># patch.json</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#000;font-weight:bold">[</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span>{<span style="color:#204a87;font-weight:bold">&#34;op&#34;: &#34;replace&#34;, &#34;path&#34;: &#34;/spec/template/containers/0/image&#34;, &#34;value&#34;: </span><span style="color:#4e9a06">&#34;registry/conatiner:1.0.0&#34;</span>}<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#000;font-weight:bold">]</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span></code></pre></div><h3 id="build-output-1">Build Output</h3>
|
||||||
|
<div class="highlight"><pre style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yaml" data-lang="yaml"><span style="color:#204a87;font-weight:bold">apiVersion</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>apps/v1<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">kind</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>Deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">metadata</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-deployment<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"></span><span style="color:#204a87;font-weight:bold">spec</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">replicas</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#0000cf;font-weight:bold">5</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">template</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">containers</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span>- <span style="color:#204a87;font-weight:bold">image</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>registry/conatiner<span style="color:#000;font-weight:bold">:</span><span style="color:#0000cf;font-weight:bold">1.0.0</span><span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span><span style="color:#f8f8f8;text-decoration:underline"> </span><span style="color:#204a87;font-weight:bold">name</span><span style="color:#000;font-weight:bold">:</span><span style="color:#f8f8f8;text-decoration:underline"> </span>the-container<span style="color:#f8f8f8;text-decoration:underline">
|
||||||
|
</span></code></pre></div>
|
||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
@@ -786,7 +859,7 @@ container number 2 of some pod template.</p>
|
|||||||
can only be placed in particular fields of
|
can only be placed in particular fields of
|
||||||
particular objects as specified by kustomize&rsquo;s
|
particular objects as specified by kustomize&rsquo;s
|
||||||
configuration data.</p>
|
configuration data.</p>
|
||||||
<p>The default config data for vars is at <a href="https://kubernetes-sigs.github.io/kustomize/konfig/builtinpluginconsts/varreference.go">/api/konfig/builtinpluginconsts/varreference.go</a>
|
<p>The default config data for vars is at <a href="https://github.com/kubernetes-sigs/kustomize/blob/master/api/konfig/builtinpluginconsts/varreference.go">/api/konfig/builtinpluginconsts/varreference.go</a>
|
||||||
Long story short, the default targets are all
|
Long story short, the default targets are all
|
||||||
container command args and env value fields.</p>
|
container command args and env value fields.</p>
|
||||||
<p>Vars should <em>not</em> be used for inserting names in
|
<p>Vars should <em>not</em> be used for inserting names in
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="namePrefix">
|
<meta itemprop="name" content="namePrefix">
|
||||||
<meta itemprop="description" content="Prepends the value to the names of all resources and references.
|
<meta itemprop="description" content="Prepends the value to the names of all resources and references.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="namePrefix"/>
|
<meta name="twitter:title" content="namePrefix"/>
|
||||||
<meta name="twitter:description" content="Prepends the value to the names of all resources and references.
|
<meta name="twitter:description" content="Prepends the value to the names of all resources and references.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
<meta name="generator" content="Hugo 0.73.0-DEV" />
|
<meta name="generator" content="Hugo 0.74.3" />
|
||||||
|
|
||||||
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
|
||||||
|
|
||||||
@@ -29,7 +29,8 @@
|
|||||||
<meta property="og:site_name" content="Kustomize" />
|
<meta property="og:site_name" content="Kustomize" />
|
||||||
<meta itemprop="name" content="namespace">
|
<meta itemprop="name" content="namespace">
|
||||||
<meta itemprop="description" content="Adds namespace to all resources.
|
<meta itemprop="description" content="Adds namespace to all resources.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="namespace"/>
|
<meta name="twitter:title" content="namespace"/>
|
||||||
<meta name="twitter:description" content="Adds namespace to all resources.
|
<meta name="twitter:description" content="Adds namespace to all resources.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user