mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-07-17 17:52:12 +00:00
Compare commits
121 Commits
drop-link-
...
updateRele
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f59f12947b | ||
|
|
90bc96d9d8 | ||
|
|
2be59aefec | ||
|
|
91b779269f | ||
|
|
e6ea4ad260 | ||
|
|
f5cab0f6e1 | ||
|
|
e39afc9f68 | ||
|
|
3801a29d9b | ||
|
|
39cf4af638 | ||
|
|
9d65dd0786 | ||
|
|
3dced70850 | ||
|
|
4356043582 | ||
|
|
c202be0338 | ||
|
|
9359155418 | ||
|
|
ba22bbe19e | ||
|
|
3e5989ae18 | ||
|
|
fbebd990a4 | ||
|
|
646e0b4f61 | ||
|
|
0f67692265 | ||
|
|
6a7afd8694 | ||
|
|
46194b3385 | ||
|
|
904a9dea08 | ||
|
|
30b58e90a3 | ||
|
|
5bdd8657a5 | ||
|
|
893c99da1c | ||
|
|
43980f8586 | ||
|
|
0c8e033c96 | ||
|
|
fa15242719 | ||
|
|
a2e080bf6c | ||
|
|
e91cdb5eba | ||
|
|
ef54f9be5a | ||
|
|
f051acb83c | ||
|
|
bbb046081b | ||
|
|
77b28a986f | ||
|
|
97bc34eb37 | ||
|
|
719380f523 | ||
|
|
640ae9521b | ||
|
|
1dffc7577b | ||
|
|
a0b7288329 | ||
|
|
cc5617c048 | ||
|
|
a77d7e5164 | ||
|
|
40dc90b3b1 | ||
|
|
16229095b3 | ||
|
|
1d91401772 | ||
|
|
007a5327d7 | ||
|
|
24beeb429d | ||
|
|
9b4d4c9d46 | ||
|
|
d5f868c5c7 | ||
|
|
ff3f39d84b | ||
|
|
451c5c32c9 | ||
|
|
faef5714bf | ||
|
|
7833c6edcf | ||
|
|
a1cd23c91d | ||
|
|
e39a5adc00 | ||
|
|
b6900ead22 | ||
|
|
d03cf061e8 | ||
|
|
8293f3002d | ||
|
|
edced4b3f6 | ||
|
|
501684a9c6 | ||
|
|
4e42e1a058 | ||
|
|
b450b624e8 | ||
|
|
0be4a61f64 | ||
|
|
596c39b7bc | ||
|
|
037ac3b134 | ||
|
|
0ff4e53046 | ||
|
|
660f7f9435 | ||
|
|
e6ee03e3e3 | ||
|
|
ca04c874f2 | ||
|
|
cbfef858a0 | ||
|
|
62fbfdfa21 | ||
|
|
5d72fbc6c9 | ||
|
|
bc37ec9d88 | ||
|
|
8619c9aa13 | ||
|
|
5d8722a786 | ||
|
|
0d5552fca6 | ||
|
|
8a8e35f3bb | ||
|
|
25dbe1eaa8 | ||
|
|
2289e7d2e9 | ||
|
|
eb0f484e3d | ||
|
|
d438271263 | ||
|
|
c4c8decb74 | ||
|
|
0590b225c7 | ||
|
|
45131a6d62 | ||
|
|
4dfe3c6296 | ||
|
|
881f358228 | ||
|
|
86c93b9fb7 | ||
|
|
25e30de2d6 | ||
|
|
a8160356bd | ||
|
|
32de6de313 | ||
|
|
501ec38777 | ||
|
|
1b2a966c62 | ||
|
|
bcdbb1a282 | ||
|
|
01f28e6779 | ||
|
|
3f8e3686e2 | ||
|
|
b47e34ea5e | ||
|
|
762e587471 | ||
|
|
6f782ac8c3 | ||
|
|
e5bc644653 | ||
|
|
1bc9225302 | ||
|
|
99d7ad6dc9 | ||
|
|
345dbc83e3 | ||
|
|
8ddf2297e8 | ||
|
|
b407675fc0 | ||
|
|
fd5eeb1645 | ||
|
|
ff5051711f | ||
|
|
51268a5f06 | ||
|
|
1366e0344a | ||
|
|
9be38e815e | ||
|
|
b71b36a213 | ||
|
|
e5a78710aa | ||
|
|
4da40461d3 | ||
|
|
c96a4f3d73 | ||
|
|
45893b2588 | ||
|
|
2f3c89e73f | ||
|
|
eee581462c | ||
|
|
bd7d0f864b | ||
|
|
b3f147d012 | ||
|
|
43b0f2d925 | ||
|
|
efd5f414a8 | ||
|
|
9b8232533f | ||
|
|
bc5859d44b |
@@ -64,7 +64,7 @@ func (p *PatchStrategicMergeTransformerPlugin) Config(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error {
|
func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
patches, err := p.h.ResmapFactory().MergePatches(p.loadedPatches)
|
patches, err := p.h.ResmapFactory().Merge(p.loadedPatches)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -331,3 +331,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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,12 +12,13 @@ require (
|
|||||||
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-20191010075000-0337d82405ff
|
||||||
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.6.1
|
||||||
sigs.k8s.io/yaml v1.2.0
|
sigs.k8s.io/yaml v1.2.0
|
||||||
)
|
)
|
||||||
|
|
||||||
replace sigs.k8s.io/kustomize/kyaml v0.5.0 => ../kyaml
|
replace sigs.k8s.io/kustomize/kyaml v0.6.1 => ../kyaml
|
||||||
|
|||||||
@@ -38,35 +38,68 @@ 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.
|
||||||
Copy() Kunstructured
|
Copy() Kunstructured
|
||||||
|
|
||||||
|
// Used by Resource.Replace, which in turn is used in many places, e.g.
|
||||||
|
// - resource.Resource.Merge
|
||||||
|
// - resWrangler.appendReplaceOrMerge (AbsorbAll)
|
||||||
|
// - api.internal.k8sdeps.transformer.patch.conflictdetector
|
||||||
GetAnnotations() map[string]string
|
GetAnnotations() map[string]string
|
||||||
GetBool(path string) (bool, error)
|
|
||||||
|
// Used by ResAccumulator and ReplacementTransformer.
|
||||||
GetFieldValue(string) (interface{}, error)
|
GetFieldValue(string) (interface{}, error)
|
||||||
GetFloat64(path string) (float64, error)
|
|
||||||
|
// Used by Resource.OrgId
|
||||||
GetGvk() resid.Gvk
|
GetGvk() resid.Gvk
|
||||||
GetInt64(path string) (int64, error)
|
|
||||||
|
// Used by resource.Factory.SliceFromBytes
|
||||||
GetKind() string
|
GetKind() string
|
||||||
|
|
||||||
|
// Used by Resource.Replace
|
||||||
GetLabels() map[string]string
|
GetLabels() map[string]string
|
||||||
GetMap(path string) (map[string]interface{}, error)
|
|
||||||
|
// Used by Resource.CurId and resource factory.
|
||||||
GetName() string
|
GetName() string
|
||||||
|
|
||||||
|
// Used by special case code in
|
||||||
|
// ResMap.SubsetThatCouldBeReferencedByResource
|
||||||
GetSlice(path string) ([]interface{}, error)
|
GetSlice(path string) ([]interface{}, error)
|
||||||
|
|
||||||
|
// GetString returns the value of a string field.
|
||||||
|
// Used by Resource.GetNamespace
|
||||||
GetString(string) (string, error)
|
GetString(string) (string, error)
|
||||||
GetStringMap(path string) (map[string]string, error)
|
|
||||||
GetStringSlice(string) ([]string, error)
|
// Several uses.
|
||||||
Map() map[string]interface{}
|
Map() map[string]interface{}
|
||||||
|
|
||||||
|
// Used by Resource.AsYAML and Resource.String
|
||||||
MarshalJSON() ([]byte, error)
|
MarshalJSON() ([]byte, error)
|
||||||
|
|
||||||
|
// Used by resWrangler.Select
|
||||||
MatchesAnnotationSelector(selector string) (bool, error)
|
MatchesAnnotationSelector(selector string) (bool, error)
|
||||||
|
|
||||||
|
// Used by resWrangler.Select
|
||||||
MatchesLabelSelector(selector string) (bool, error)
|
MatchesLabelSelector(selector string) (bool, error)
|
||||||
Patch(Kunstructured) error
|
|
||||||
|
// Used by Resource.Replace.
|
||||||
SetAnnotations(map[string]string)
|
SetAnnotations(map[string]string)
|
||||||
|
|
||||||
|
// Used by PatchStrategicMergeTransformer.
|
||||||
SetGvk(resid.Gvk)
|
SetGvk(resid.Gvk)
|
||||||
|
|
||||||
|
// Used by Resource.Replace and used to remove "validated by" labels.
|
||||||
SetLabels(map[string]string)
|
SetLabels(map[string]string)
|
||||||
SetMap(map[string]interface{})
|
|
||||||
|
// Used by Resource.Replace.
|
||||||
SetName(string)
|
SetName(string)
|
||||||
|
|
||||||
|
// Used by Resource.Replace.
|
||||||
SetNamespace(string)
|
SetNamespace(string)
|
||||||
|
|
||||||
|
// Needed, for now, by kyaml/filtersutil.ApplyToJSON.
|
||||||
UnmarshalJSON([]byte) error
|
UnmarshalJSON([]byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -338,6 +338,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=
|
||||||
@@ -523,8 +524,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.6.1 h1:mwffj5vt3MPdbWV3fZnnwol8SO7sUoGdgejBlvseyak=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
sigs.k8s.io/kustomize/kyaml v0.6.1/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
||||||
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=
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package patch
|
package merge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -20,18 +20,20 @@ import (
|
|||||||
|
|
||||||
type conflictDetector interface {
|
type conflictDetector interface {
|
||||||
hasConflict(patch1, patch2 *resource.Resource) (bool, error)
|
hasConflict(patch1, patch2 *resource.Resource) (bool, error)
|
||||||
findConflict(conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error)
|
findConflict(
|
||||||
|
conflictingPatchIdx int,
|
||||||
|
patches []*resource.Resource) (*resource.Resource, error)
|
||||||
mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error)
|
mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonMergePatch struct {
|
type jsonMergePatch struct {
|
||||||
rf *resource.Factory
|
resourceFactory *resource.Factory
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ conflictDetector = &jsonMergePatch{}
|
var _ conflictDetector = &jsonMergePatch{}
|
||||||
|
|
||||||
func newJMPConflictDetector(rf *resource.Factory) conflictDetector {
|
func newJMPConflictDetector(rf *resource.Factory) conflictDetector {
|
||||||
return &jsonMergePatch{rf: rf}
|
return &jsonMergePatch{resourceFactory: rf}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jmp *jsonMergePatch) hasConflict(
|
func (jmp *jsonMergePatch) hasConflict(
|
||||||
@@ -40,7 +42,8 @@ func (jmp *jsonMergePatch) hasConflict(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (jmp *jsonMergePatch) findConflict(
|
func (jmp *jsonMergePatch) findConflict(
|
||||||
conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error) {
|
conflictingPatchIdx int,
|
||||||
|
patches []*resource.Resource) (*resource.Resource, error) {
|
||||||
for i, patch := range patches {
|
for i, patch := range patches {
|
||||||
if i == conflictingPatchIdx {
|
if i == conflictingPatchIdx {
|
||||||
continue
|
continue
|
||||||
@@ -77,7 +80,7 @@ func (jmp *jsonMergePatch) mergePatches(
|
|||||||
}
|
}
|
||||||
mergedMap := make(map[string]interface{})
|
mergedMap := make(map[string]interface{})
|
||||||
err = json.Unmarshal(mergedBytes, &mergedMap)
|
err = json.Unmarshal(mergedBytes, &mergedMap)
|
||||||
return jmp.rf.FromMap(mergedMap), err
|
return jmp.resourceFactory.FromMap(mergedMap), err
|
||||||
}
|
}
|
||||||
|
|
||||||
type strategicMergePatch struct {
|
type strategicMergePatch struct {
|
||||||
@@ -94,13 +97,15 @@ func newSMPConflictDetector(
|
|||||||
return &strategicMergePatch{lookupPatchMeta: lookupPatchMeta, rf: rf}, err
|
return &strategicMergePatch{lookupPatchMeta: lookupPatchMeta, rf: rf}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *strategicMergePatch) hasConflict(p1, p2 *resource.Resource) (bool, error) {
|
func (smp *strategicMergePatch) hasConflict(
|
||||||
|
p1, p2 *resource.Resource) (bool, error) {
|
||||||
return strategicpatch.MergingMapsHaveConflicts(
|
return strategicpatch.MergingMapsHaveConflicts(
|
||||||
p1.Map(), p2.Map(), smp.lookupPatchMeta)
|
p1.Map(), p2.Map(), smp.lookupPatchMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *strategicMergePatch) findConflict(
|
func (smp *strategicMergePatch) findConflict(
|
||||||
conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error) {
|
conflictingPatchIdx int,
|
||||||
|
patches []*resource.Resource) (*resource.Resource, error) {
|
||||||
for i, patch := range patches {
|
for i, patch := range patches {
|
||||||
if i == conflictingPatchIdx {
|
if i == conflictingPatchIdx {
|
||||||
continue
|
continue
|
||||||
@@ -122,10 +127,12 @@ func (smp *strategicMergePatch) findConflict(
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (smp *strategicMergePatch) mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error) {
|
func (smp *strategicMergePatch) mergePatches(
|
||||||
|
patch1, patch2 *resource.Resource) (*resource.Resource, error) {
|
||||||
if hasDeleteDirectiveMarker(patch2.Map()) {
|
if hasDeleteDirectiveMarker(patch2.Map()) {
|
||||||
if hasDeleteDirectiveMarker(patch1.Map()) {
|
if hasDeleteDirectiveMarker(patch1.Map()) {
|
||||||
return nil, fmt.Errorf("cannot merge patches both containing '$patch: delete' directives")
|
return nil, fmt.Errorf(
|
||||||
|
"cannot merge patches both containing '$patch: delete' directives")
|
||||||
}
|
}
|
||||||
patch1, patch2 = patch2, patch1
|
patch1, patch2 = patch2, patch1
|
||||||
}
|
}
|
||||||
@@ -134,10 +141,21 @@ func (smp *strategicMergePatch) mergePatches(patch1, patch2 *resource.Resource)
|
|||||||
return smp.rf.FromMap(mergeJSONMap), err
|
return smp.rf.FromMap(mergeJSONMap), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergePatches merge and index patches by OrgId.
|
type merginatorImpl struct {
|
||||||
// It errors out if there is conflict between patches.
|
rf *resource.Factory
|
||||||
func MergePatches(patches []*resource.Resource,
|
}
|
||||||
rf *resource.Factory) (resmap.ResMap, error) {
|
|
||||||
|
// NewMerginator returns a new implementation of resmap.Merginator.
|
||||||
|
func NewMerginator(rf *resource.Factory) resmap.Merginator {
|
||||||
|
return &merginatorImpl{rf: rf}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ resmap.Merginator = (*merginatorImpl)(nil)
|
||||||
|
|
||||||
|
// Merge merges the incoming resources into a new resmap.ResMap.
|
||||||
|
// Returns an error on conflict.
|
||||||
|
func (m *merginatorImpl) Merge(
|
||||||
|
patches []*resource.Resource) (resmap.ResMap, error) {
|
||||||
rc := resmap.New()
|
rc := resmap.New()
|
||||||
for ix, patch := range patches {
|
for ix, patch := range patches {
|
||||||
id := patch.OrgId()
|
id := patch.OrgId()
|
||||||
@@ -156,9 +174,9 @@ func MergePatches(patches []*resource.Resource,
|
|||||||
}
|
}
|
||||||
var cd conflictDetector
|
var cd conflictDetector
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cd = newJMPConflictDetector(rf)
|
cd = newJMPConflictDetector(m.rf)
|
||||||
} else {
|
} else {
|
||||||
cd, err = newSMPConflictDetector(versionedObj, rf)
|
cd, err = newSMPConflictDetector(versionedObj, m.rf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Package transformer provides transformer factory
|
|
||||||
package transformer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer/patch"
|
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FactoryImpl makes patch transformer and name hash transformer
|
|
||||||
type FactoryImpl struct{}
|
|
||||||
|
|
||||||
// NewFactoryImpl makes a new factoryImpl instance
|
|
||||||
func NewFactoryImpl() *FactoryImpl {
|
|
||||||
return &FactoryImpl{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *FactoryImpl) MergePatches(patches []*resource.Resource,
|
|
||||||
rf *resource.Factory) (
|
|
||||||
resmap.ResMap, error) {
|
|
||||||
return patch.MergePatches(patches, rf)
|
|
||||||
}
|
|
||||||
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
|
||||||
@@ -173,11 +173,11 @@ func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) {
|
|||||||
// TODO(donnyxia): This is actually not used by generator and only used to bypass a kio limitation.
|
// TODO(donnyxia): This is actually not used by generator and only used to bypass a kio limitation.
|
||||||
// Need better solution.
|
// Need better solution.
|
||||||
if input == nil {
|
if input == nil {
|
||||||
yaml, err := functionConfig.String()
|
yml, err := functionConfig.String()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
input = []byte(yaml)
|
input = []byte(yml)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure and Execute Fn. We don't need to convert resources to ResourceList here
|
// Configure and Execute Fn. We don't need to convert resources to ResourceList here
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/transform"
|
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
@@ -28,7 +27,6 @@ type KustTarget struct {
|
|||||||
ldr ifc.Loader
|
ldr ifc.Loader
|
||||||
validator ifc.Validator
|
validator ifc.Validator
|
||||||
rFactory *resmap.Factory
|
rFactory *resmap.Factory
|
||||||
tFactory resmap.PatchFactory
|
|
||||||
pLdr *loader.Loader
|
pLdr *loader.Loader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,13 +35,11 @@ func NewKustTarget(
|
|||||||
ldr ifc.Loader,
|
ldr ifc.Loader,
|
||||||
validator ifc.Validator,
|
validator ifc.Validator,
|
||||||
rFactory *resmap.Factory,
|
rFactory *resmap.Factory,
|
||||||
tFactory resmap.PatchFactory,
|
|
||||||
pLdr *loader.Loader) *KustTarget {
|
pLdr *loader.Loader) *KustTarget {
|
||||||
return &KustTarget{
|
return &KustTarget{
|
||||||
ldr: ldr,
|
ldr: ldr,
|
||||||
validator: validator,
|
validator: validator,
|
||||||
rFactory: rFactory,
|
rFactory: rFactory,
|
||||||
tFactory: tFactory,
|
|
||||||
pLdr: pLdr,
|
pLdr: pLdr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,8 +254,7 @@ func (kt *KustTarget) runTransformers(ra *accumulator.ResAccumulator) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
r = append(r, lts...)
|
r = append(r, lts...)
|
||||||
t := transform.NewMultiTransformer(r)
|
return ra.Transform(newMultiTransformer(r))
|
||||||
return ra.Transform(t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]resmap.Transformer, error) {
|
func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]resmap.Transformer, error) {
|
||||||
@@ -294,7 +289,6 @@ func (kt *KustTarget) runValidators(ra *accumulator.ResAccumulator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) {
|
func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) {
|
||||||
|
|
||||||
resources := rm.Resources()
|
resources := rm.Resources()
|
||||||
for _, r := range resources {
|
for _, r := range resources {
|
||||||
labels := r.GetLabels()
|
labels := r.GetLabels()
|
||||||
@@ -353,8 +347,7 @@ func (kt *KustTarget) accumulateComponents(
|
|||||||
func (kt *KustTarget) accumulateDirectory(
|
func (kt *KustTarget) accumulateDirectory(
|
||||||
ra *accumulator.ResAccumulator, ldr ifc.Loader, isComponent bool) (*accumulator.ResAccumulator, error) {
|
ra *accumulator.ResAccumulator, ldr ifc.Loader, isComponent bool) (*accumulator.ResAccumulator, error) {
|
||||||
defer ldr.Cleanup()
|
defer ldr.Cleanup()
|
||||||
subKt := NewKustTarget(
|
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
|
||||||
ldr, kt.validator, kt.rFactory, kt.tFactory, kt.pLdr)
|
|
||||||
err := subKt.Load()
|
err := subKt.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(
|
return nil, errors.Wrapf(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer"
|
"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/kunstruct"
|
||||||
@@ -35,17 +35,17 @@ func makeKustTargetWithRf(
|
|||||||
t *testing.T,
|
t *testing.T,
|
||||||
fSys filesys.FileSystem,
|
fSys filesys.FileSystem,
|
||||||
root string,
|
root string,
|
||||||
resFact *resource.Factory) *target.KustTarget {
|
resourceFactory *resource.Factory) *target.KustTarget {
|
||||||
rf := resmap.NewFactory(resFact, transformer.NewFactoryImpl())
|
|
||||||
pc := konfig.DisabledPluginConfig()
|
|
||||||
ldr, err := fLdr.NewLoader(fLdr.RestrictionRootOnly, root, fSys)
|
ldr, err := fLdr.NewLoader(fLdr.RestrictionRootOnly, root, fSys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
rf := resmap.NewFactory(
|
||||||
|
resourceFactory, merge.NewMerginator(resourceFactory))
|
||||||
|
pc := konfig.DisabledPluginConfig()
|
||||||
return target.NewKustTarget(
|
return target.NewKustTarget(
|
||||||
ldr,
|
ldr,
|
||||||
valtest_test.MakeFakeValidator(),
|
valtest_test.MakeFakeValidator(),
|
||||||
rf,
|
rf,
|
||||||
transformer.NewFactoryImpl(),
|
|
||||||
pLdr.NewLoader(pc, rf))
|
pLdr.NewLoader(pc, rf))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package transform
|
package target
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -17,8 +17,8 @@ type multiTransformer struct {
|
|||||||
|
|
||||||
var _ resmap.Transformer = &multiTransformer{}
|
var _ resmap.Transformer = &multiTransformer{}
|
||||||
|
|
||||||
// NewMultiTransformer constructs a multiTransformer.
|
// newMultiTransformer constructs a multiTransformer.
|
||||||
func NewMultiTransformer(t []resmap.Transformer) resmap.Transformer {
|
func newMultiTransformer(t []resmap.Transformer) resmap.Transformer {
|
||||||
r := &multiTransformer{
|
r := &multiTransformer{
|
||||||
transformers: make([]resmap.Transformer, len(t)),
|
transformers: make([]resmap.Transformer, len(t)),
|
||||||
checkConflictEnabled: false}
|
checkConflictEnabled: false}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -466,278 +466,6 @@ func TestGetString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetInt64(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pathToField string
|
|
||||||
expectedValue int64
|
|
||||||
errorExpected bool
|
|
||||||
errorMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "numberAsValue",
|
|
||||||
pathToField: "this.is.aNumber",
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: int64(1000),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldIndexSubfield",
|
|
||||||
pathToField: "complextree[1].field2[1].intsubfield",
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: int64(1121),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "twoFieldsOneMissing",
|
|
||||||
pathToField: "metadata.banana",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'metadata.banana'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldOutOfBoundIndex",
|
|
||||||
pathToField: "these[1].field2[99]",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'these[1].field2[99]'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validDownwardAPISpecs",
|
|
||||||
pathToField: `spec.ports['port']`,
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: int64(80),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
s, err := kunstructured.GetInt64(test.pathToField)
|
|
||||||
if test.errorExpected {
|
|
||||||
compareExpectedError(t, test.name, test.pathToField, err, test.errorMsg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
unExpectedError(t, test.name, test.pathToField, err)
|
|
||||||
}
|
|
||||||
compareValues(t, test.name, test.pathToField, test.expectedValue, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetFloat64(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pathToField string
|
|
||||||
expectedValue float64
|
|
||||||
errorExpected bool
|
|
||||||
errorMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "floatAsValue",
|
|
||||||
pathToField: "this.is.aFloat",
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: float64(1.001),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldIndexSubfield",
|
|
||||||
pathToField: "complextree[1].field2[1].floatsubfield",
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: float64(1.1121),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validDownwardAPIThis",
|
|
||||||
pathToField: `this.is[aFloat]`,
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: float64(1.001),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "twoFieldsOneMissing",
|
|
||||||
pathToField: "metadata.banana",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'metadata.banana'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldOutOfBoundIndex",
|
|
||||||
pathToField: "these[1].field2[99]",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "index 99 is out of bounds",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
s, err := kunstructured.GetFloat64(test.pathToField)
|
|
||||||
if test.errorExpected {
|
|
||||||
compareExpectedError(t, test.name, test.pathToField, err, test.errorMsg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
unExpectedError(t, test.name, test.pathToField, err)
|
|
||||||
}
|
|
||||||
compareValues(t, test.name, test.pathToField, test.expectedValue, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetBool(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pathToField string
|
|
||||||
expectedValue bool
|
|
||||||
errorExpected bool
|
|
||||||
errorMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "boolAsValue",
|
|
||||||
pathToField: "this.is.aBool",
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldIndexSubfield",
|
|
||||||
pathToField: "complextree[1].field2[1].boolfield",
|
|
||||||
errorExpected: false,
|
|
||||||
expectedValue: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "twoFieldsOneMissing",
|
|
||||||
pathToField: "metadata.banana",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'metadata.banana'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldOutOfBoundIndex",
|
|
||||||
pathToField: "these[1].field2[99]",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'these[1].field2[99]'",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
s, err := kunstructured.GetBool(test.pathToField)
|
|
||||||
if test.errorExpected {
|
|
||||||
compareExpectedError(t, test.name, test.pathToField, err, test.errorMsg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
unExpectedError(t, test.name, test.pathToField, err)
|
|
||||||
}
|
|
||||||
compareValues(t, test.name, test.pathToField, test.expectedValue, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetStringMap(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pathToField string
|
|
||||||
errorExpected bool
|
|
||||||
errorMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "validStringMap",
|
|
||||||
pathToField: "those[2]",
|
|
||||||
errorExpected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "twoFieldsOneMissing",
|
|
||||||
pathToField: "metadata.banana",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'metadata.banana'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldOutOfBoundIndex",
|
|
||||||
pathToField: "these[1].field2[99]",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'these[1].field2[99]'",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := kunstructured.GetStringMap(test.pathToField)
|
|
||||||
if test.errorExpected {
|
|
||||||
compareExpectedError(t, test.name, test.pathToField, err, test.errorMsg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
unExpectedError(t, test.name, test.pathToField, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetMap(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pathToField string
|
|
||||||
errorExpected bool
|
|
||||||
errorMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "validMap",
|
|
||||||
pathToField: "those[2]",
|
|
||||||
errorExpected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldIndexSubfield",
|
|
||||||
pathToField: "complextree[1].field2[1]",
|
|
||||||
errorExpected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "twoFieldsOneMissing",
|
|
||||||
pathToField: "metadata.banana",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'metadata.banana'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldOutOfBoundIndex",
|
|
||||||
pathToField: "these[1].field2[99]",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'these[1].field2[99]'",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := kunstructured.GetMap(test.pathToField)
|
|
||||||
if test.errorExpected {
|
|
||||||
compareExpectedError(t, test.name, test.pathToField, err, test.errorMsg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
unExpectedError(t, test.name, test.pathToField, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetStringSlice(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pathToField string
|
|
||||||
errorExpected bool
|
|
||||||
errorMsg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "validStringSlice",
|
|
||||||
pathToField: "that",
|
|
||||||
errorExpected: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "twoFieldsOneMissing",
|
|
||||||
pathToField: "metadata.banana",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'metadata.banana'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validStructSubFieldOutOfBoundIndex",
|
|
||||||
pathToField: "these[1].field2[99]",
|
|
||||||
errorExpected: true,
|
|
||||||
errorMsg: "no field named 'these[1].field2[99]'",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
_, err := kunstructured.GetStringSlice(test.pathToField)
|
|
||||||
if test.errorExpected {
|
|
||||||
compareExpectedError(t, test.name, test.pathToField, err, test.errorMsg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
unExpectedError(t, test.name, test.pathToField, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetSlice(t *testing.T) {
|
func TestGetSlice(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
@@ -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{}
|
||||||
@@ -12,5 +12,9 @@ namespace:
|
|||||||
kind: RoleBinding
|
kind: RoleBinding
|
||||||
- path: subjects
|
- path: subjects
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
|
- path: spec/service/namespace
|
||||||
|
group: apiregistration.k8s.io
|
||||||
|
kind: APIService
|
||||||
|
create: true
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|||||||
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/transformer"
|
|
||||||
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) {
|
||||||
pf := transformer.NewFactoryImpl()
|
resmapFactory := resmap.NewFactory(
|
||||||
rf := resmap.NewFactory(
|
b.depProvider.GetResourceFactory(),
|
||||||
resource.NewFactory(
|
b.depProvider.GetMerginator())
|
||||||
kunstruct.NewKunstructuredFactoryImpl()),
|
|
||||||
pf)
|
|
||||||
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,10 +65,9 @@ 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(),
|
||||||
rf,
|
resmapFactory,
|
||||||
pf,
|
pLdr.NewLoader(b.options.PluginConfig, resmapFactory),
|
||||||
pLdr.NewLoader(b.options.PluginConfig, rf),
|
|
||||||
)
|
)
|
||||||
err = kt.Load()
|
err = kt.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -84,7 +83,9 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
|
|||||||
}
|
}
|
||||||
if b.options.AddManagedbyLabel {
|
if b.options.AddManagedbyLabel {
|
||||||
t := builtins.LabelTransformerPlugin{
|
t := builtins.LabelTransformerPlugin{
|
||||||
Labels: map[string]string{konfig.ManagedbyLabelKey: fmt.Sprintf("kustomize-%s", provenance.GetProvenance().Version)},
|
Labels: map[string]string{
|
||||||
|
konfig.ManagedbyLabelKey: fmt.Sprintf(
|
||||||
|
"kustomize-%s", provenance.GetProvenance().Version)},
|
||||||
FieldSpecs: []types.FieldSpec{{
|
FieldSpecs: []types.FieldSpec{{
|
||||||
Path: "metadata/labels",
|
Path: "metadata/labels",
|
||||||
CreateIfNotPresent: true,
|
CreateIfNotPresent: true,
|
||||||
|
|||||||
113
api/krusty/nameprefixsuffixpatch_test.go
Normal file
113
api/krusty/nameprefixsuffixpatch_test.go
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package krusty_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Coverage for issue #2609
|
||||||
|
func TestNamePrefixSuffixPatch(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
|
||||||
|
th.WriteF("handlers/kustomization.yaml", `
|
||||||
|
nameSuffix: -suffix
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("handlers/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: short
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: handler
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("mysql/kustomization.yaml", `
|
||||||
|
configMapGenerator:
|
||||||
|
- name: mysql
|
||||||
|
literals:
|
||||||
|
- MYSQL_USER=default
|
||||||
|
- MYSQL_DATABASE=default
|
||||||
|
- PORT=3306
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK(".", `
|
||||||
|
resources:
|
||||||
|
- mysql
|
||||||
|
- handlers
|
||||||
|
|
||||||
|
configMapGenerator:
|
||||||
|
- name: mysql
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- MYSQL_DATABASE=db
|
||||||
|
- MYSQL_USER=my-user
|
||||||
|
- MYSQL_PASSWORD='correct horse battery staple'
|
||||||
|
|
||||||
|
patches:
|
||||||
|
- target:
|
||||||
|
kind: Deployment
|
||||||
|
name: s.*
|
||||||
|
patch: |-
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ignored
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: handler
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mysql
|
||||||
|
env:
|
||||||
|
- valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: mysql
|
||||||
|
key: MYSQL_DATABASE
|
||||||
|
`)
|
||||||
|
|
||||||
|
m := th.Run(".", th.MakeDefaultOptions())
|
||||||
|
// Per #2609, the desired behavior is for configMapRef.name and configMapKeyRef.name to be "mysql-9792mdchtg" not "mysql"
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
MYSQL_DATABASE: db
|
||||||
|
MYSQL_PASSWORD: correct horse battery staple
|
||||||
|
MYSQL_USER: my-user
|
||||||
|
PORT: "3306"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations: {}
|
||||||
|
labels: {}
|
||||||
|
name: mysql-9792mdchtg
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: short-suffix
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: MYSQL_DATABASE
|
||||||
|
name: mysql-9792mdchtg
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: mysql-9792mdchtg
|
||||||
|
name: handler
|
||||||
|
`)
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
79
api/krusty/stringquoteblank_test.go
Normal file
79
api/krusty/stringquoteblank_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package krusty_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This test is for output string style.
|
||||||
|
// Currently all quotes will be removed if the string is valid as plain (unquoted) style.
|
||||||
|
// If a string cannot be unquoted, it will be put into a pair of single quotes.
|
||||||
|
// See https://yaml.org/spec/1.2/spec.html#id2788859 for more details about what kind of string
|
||||||
|
// is invalid as plain style.
|
||||||
|
func TestLongLineBreaks(t *testing.T) {
|
||||||
|
th := kusttest_test.MakeHarness(t)
|
||||||
|
th.WriteF("/app/deployment.yaml", `
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: mariadb
|
||||||
|
image: test
|
||||||
|
env:
|
||||||
|
- name: SHORT_STRING
|
||||||
|
value: short_string
|
||||||
|
- name: SHORT_STRING_WITH_SINGLE_QUOTE
|
||||||
|
value: 'short_string'
|
||||||
|
- name: SHORT_STRING_WITH_DOUBLE_QUOTE
|
||||||
|
value: "short_string"
|
||||||
|
- name: SHORT_STRING_BLANK
|
||||||
|
value: short string
|
||||||
|
- name: LONG_STRING_BLANK
|
||||||
|
value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius.
|
||||||
|
- name: LONG_STRING_BLANK_WITH_SINGLE_QUOTE
|
||||||
|
value: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius.'
|
||||||
|
- name: LONG_STRING_BLANK_WITH_DOUBLE_QUOTE
|
||||||
|
value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius."
|
||||||
|
- name: INVALID_PLAIN_STYLE_STRING
|
||||||
|
value: ': test'
|
||||||
|
`)
|
||||||
|
th.WriteK("/app", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
`)
|
||||||
|
m := th.Run("/app", th.MakeDefaultOptions())
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: test
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: SHORT_STRING
|
||||||
|
value: short_string
|
||||||
|
- name: SHORT_STRING_WITH_SINGLE_QUOTE
|
||||||
|
value: short_string
|
||||||
|
- name: SHORT_STRING_WITH_DOUBLE_QUOTE
|
||||||
|
value: short_string
|
||||||
|
- name: SHORT_STRING_BLANK
|
||||||
|
value: short string
|
||||||
|
- name: LONG_STRING_BLANK
|
||||||
|
value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius.
|
||||||
|
- name: LONG_STRING_BLANK_WITH_SINGLE_QUOTE
|
||||||
|
value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius.
|
||||||
|
- name: LONG_STRING_BLANK_WITH_DOUBLE_QUOTE
|
||||||
|
value: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas suscipit ex non molestie varius.
|
||||||
|
- name: INVALID_PLAIN_STYLE_STRING
|
||||||
|
value: ': test'
|
||||||
|
image: test
|
||||||
|
name: mariadb
|
||||||
|
`)
|
||||||
|
}
|
||||||
@@ -199,7 +199,9 @@ func (fl *fileLoader) New(path string) (ifc.Loader, error) {
|
|||||||
}
|
}
|
||||||
root, errDir := demandDirectoryRoot(fl.fSys, fl.root.Join(path))
|
root, errDir := demandDirectoryRoot(fl.fSys, fl.root.Join(path))
|
||||||
if errDir != nil {
|
if errDir != nil {
|
||||||
return nil, fmt.Errorf("Error loading %s with git: %v, dir: %v, get: %v", path, errGit, errDir, errGet)
|
return nil, fmt.Errorf(
|
||||||
|
"error loading %s with git: %v, dir: %v, get: %v",
|
||||||
|
path, errGit, errDir, errGet)
|
||||||
}
|
}
|
||||||
if errDir := fl.errIfGitContainmentViolation(root); errDir != nil {
|
if errDir := fl.errIfGitContainmentViolation(root); errDir != nil {
|
||||||
return nil, errDir
|
return nil, errDir
|
||||||
|
|||||||
@@ -39,5 +39,7 @@ func NewLoader(
|
|||||||
return newLoaderAtConfirmedDir(lr, root, fSys, nil, git.ClonerUsingGitExec, getRemoteTarget), nil
|
return newLoaderAtConfirmedDir(lr, root, fSys, nil, git.ClonerUsingGitExec, getRemoteTarget), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("Error creating new loader with git: %v, dir: %v, get: %v", errGit, errDir, errGet)
|
return nil, fmt.Errorf(
|
||||||
|
"error creating new loader with git: %v, dir: %v, get: %v",
|
||||||
|
errGit, errDir, errGet)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,15 @@ func FromKind(k string) Gvk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GvkFromString makes a Gvk with a string,
|
// ParseGroupVersion parses a KRM metadata apiVersion field.
|
||||||
// which is constructed by String() function
|
func ParseGroupVersion(apiVersion string) (group, version string) {
|
||||||
|
if i := strings.Index(apiVersion, "/"); i > -1 {
|
||||||
|
return apiVersion[:i], apiVersion[i+1:]
|
||||||
|
}
|
||||||
|
return "", apiVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
// GvkFromString makes a Gvk from the output of Gvk.String().
|
||||||
func GvkFromString(s string) Gvk {
|
func GvkFromString(s string) Gvk {
|
||||||
values := strings.Split(s, fieldSep)
|
values := strings.Split(s, fieldSep)
|
||||||
g := values[0]
|
g := values[0]
|
||||||
|
|||||||
@@ -113,6 +113,30 @@ func TestString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseGroupVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
g string
|
||||||
|
v string
|
||||||
|
}{
|
||||||
|
{input: "", g: "", v: ""},
|
||||||
|
{input: "v1", g: "", v: "v1"},
|
||||||
|
{input: "apps/v1", g: "apps", v: "v1"},
|
||||||
|
{input: "/v1", g: "", v: "v1"},
|
||||||
|
{input: "apps/", g: "apps", v: ""},
|
||||||
|
{input: "/apps/", g: "", v: "apps/"},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
g, v := ParseGroupVersion(tc.input)
|
||||||
|
if g != tc.g {
|
||||||
|
t.Errorf("%s: expected group '%s', got '%s'", tc.input, tc.g, g)
|
||||||
|
}
|
||||||
|
if v != tc.v {
|
||||||
|
t.Errorf("%s: expected version '%s', got '%s'", tc.input, tc.v, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSelectByGVK(t *testing.T) {
|
func TestSelectByGVK(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
description string
|
description string
|
||||||
|
|||||||
@@ -11,15 +11,24 @@ 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
|
||||||
tf PatchFactory
|
// Makes ResMaps via merging.
|
||||||
|
pm Merginator
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFactory returns a new resmap.Factory.
|
// NewFactory returns a new resmap.Factory.
|
||||||
func NewFactory(rf *resource.Factory, tf PatchFactory) *Factory {
|
func NewFactory(rf *resource.Factory, pm Merginator) *Factory {
|
||||||
return &Factory{resF: rf, tf: tf}
|
return &Factory{resF: rf, pm: pm}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RF returns a resource.Factory.
|
// RF returns a resource.Factory.
|
||||||
@@ -87,6 +96,7 @@ func (rmF *Factory) NewResMapFromConfigMapArgs(
|
|||||||
return newResMapFromResourceSlice(resources)
|
return newResMapFromResourceSlice(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromConfigMapArgs creates a new ResMap containing one ConfigMap.
|
||||||
func (rmF *Factory) FromConfigMapArgs(
|
func (rmF *Factory) FromConfigMapArgs(
|
||||||
kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) {
|
kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) {
|
||||||
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
||||||
@@ -111,6 +121,7 @@ func (rmF *Factory) NewResMapFromSecretArgs(
|
|||||||
return newResMapFromResourceSlice(resources)
|
return newResMapFromResourceSlice(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromSecretArgs creates a new ResMap containing one secret.
|
||||||
func (rmF *Factory) FromSecretArgs(
|
func (rmF *Factory) FromSecretArgs(
|
||||||
kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) {
|
kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) {
|
||||||
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
||||||
@@ -120,12 +131,14 @@ func (rmF *Factory) FromSecretArgs(
|
|||||||
return rmF.FromResource(res), nil
|
return rmF.FromResource(res), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rmF *Factory) MergePatches(patches []*resource.Resource) (
|
// Merge creates a new ResMap by merging incoming resources.
|
||||||
ResMap, error) {
|
// Error if conflict found.
|
||||||
return rmF.tf.MergePatches(patches, rmF.resF)
|
func (rmF *Factory) Merge(patches []*resource.Resource) (ResMap, error) {
|
||||||
|
return rmF.pm.Merge(patches)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResMapFromResourceSlice(resources []*resource.Resource) (ResMap, error) {
|
func newResMapFromResourceSlice(
|
||||||
|
resources []*resource.Resource) (ResMap, error) {
|
||||||
result := New()
|
result := New()
|
||||||
for _, res := range resources {
|
for _, res := range resources {
|
||||||
err := result.Append(res)
|
err := result.Append(res)
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
/// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Package patch holds miscellaneous interfaces used by kustomize.
|
|
||||||
package resmap
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PatchFactory makes transformers that require k8sdeps.
|
|
||||||
type PatchFactory interface {
|
|
||||||
MergePatches(patches []*resource.Resource,
|
|
||||||
rf *resource.Factory) (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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,55 +14,9 @@ import (
|
|||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Resource is a representation of a Kubernetes Resource Model object paired
|
// Resource is a representation of a Kubernetes Resource Model (KRM) object
|
||||||
// with metadata used by kustomize.
|
// paired with metadata used by kustomize.
|
||||||
//
|
// For more history, see sigs.k8s.io/kustomize/api/ifc.Unstructured
|
||||||
// At time of writing Resource is changing from being based on an object
|
|
||||||
// sitting behind
|
|
||||||
//
|
|
||||||
// sigs.k8s.io/kustomize/api/ifc.Unstructured
|
|
||||||
//
|
|
||||||
// to being based on an instance of
|
|
||||||
//
|
|
||||||
// sigs.k8s.io/kustomize/kyaml/yaml.RNode
|
|
||||||
//
|
|
||||||
// Ultimately, use of the Resource struct in kustomize should be entirely
|
|
||||||
// replaced by instances of RNode for direct use in kyaml, with all metadata
|
|
||||||
// stored directly in the RNodes as short-lived annotations, to be deleted
|
|
||||||
// before final output as part of a final filtration step. Using annotations
|
|
||||||
// will allow pipelined KRM Config Functions to share metadata that would
|
|
||||||
// otherwise be hidden in kustomize-only structures.
|
|
||||||
//
|
|
||||||
// ifc.Unstructured is an interface hiding an instance of
|
|
||||||
// sigs.k8s.io/kustomize/api/k8sdeps/kunstruct.UnstructAdapter
|
|
||||||
// which in turn adapts an instance of
|
|
||||||
// k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
|
|
||||||
// to the ifc.Unstructured interface. This latter code handles
|
|
||||||
// mutations in the old code.
|
|
||||||
//
|
|
||||||
// The rule in kustomize development has been
|
|
||||||
// api/
|
|
||||||
// k8sdeps/ ifc/ krusty/ theRest/
|
|
||||||
//
|
|
||||||
// 1) Depend on k8s.io/ only via sigs.k8s.io/kustomize/api/k8sdeps.
|
|
||||||
//
|
|
||||||
// 2) Instances created in k8sdeps/ can only be injected into theRest/
|
|
||||||
// behind interfaces defined in ifc/, arranged by a small bit of code
|
|
||||||
// in krusty/. Nothing in theRest/ can import k8sdeps/.
|
|
||||||
//
|
|
||||||
// This was to allow for importing kustomize code into kubectl, which also
|
|
||||||
// depends on k8s.io/apimachinery, albeit via a strange, old arrangement
|
|
||||||
// predating Go modules. The idea was to copy the k8sdeps/ tree into kubectl
|
|
||||||
// via a large PR, and depend on the rest via vendoring (and a small copy of
|
|
||||||
// krusty/ code). Over 2019, however, kubectl underwent large code changes
|
|
||||||
// including a switch to Go modules, and an attempt to extract it from the
|
|
||||||
// k8s repo, and this made large kustomize integration PRs hard to get through
|
|
||||||
// code review.
|
|
||||||
//
|
|
||||||
// The new plan is to eliminate k8sdeps/ entirely, along with its k8s.io/
|
|
||||||
// dependence, switch to kyaml, and thus allow kustomize to be imported into
|
|
||||||
// kubectl via normal Go module imports.
|
|
||||||
//
|
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
kunStr ifc.Kunstructured
|
kunStr ifc.Kunstructured
|
||||||
originalName string
|
originalName string
|
||||||
@@ -75,7 +29,7 @@ type Resource struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) ResetPrimaryData(incoming *Resource) {
|
func (r *Resource) ResetPrimaryData(incoming *Resource) {
|
||||||
r.kunStr = incoming.kunStr.Copy()
|
r.kunStr = incoming.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) GetAnnotations() map[string]string {
|
func (r *Resource) GetAnnotations() map[string]string {
|
||||||
@@ -86,26 +40,14 @@ func (r *Resource) Copy() ifc.Kunstructured {
|
|||||||
return r.kunStr.Copy()
|
return r.kunStr.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) GetBool(p string) (bool, error) {
|
|
||||||
return r.kunStr.GetBool(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) GetFieldValue(f string) (interface{}, error) {
|
func (r *Resource) GetFieldValue(f string) (interface{}, error) {
|
||||||
return r.kunStr.GetFieldValue(f)
|
return r.kunStr.GetFieldValue(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) GetFloat64(p string) (float64, error) {
|
|
||||||
return r.kunStr.GetFloat64(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) GetGvk() resid.Gvk {
|
func (r *Resource) GetGvk() resid.Gvk {
|
||||||
return r.kunStr.GetGvk()
|
return r.kunStr.GetGvk()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) GetInt64(p string) (int64, error) {
|
|
||||||
return r.kunStr.GetInt64(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) GetKind() string {
|
func (r *Resource) GetKind() string {
|
||||||
return r.kunStr.GetKind()
|
return r.kunStr.GetKind()
|
||||||
}
|
}
|
||||||
@@ -114,10 +56,6 @@ func (r *Resource) GetLabels() map[string]string {
|
|||||||
return r.kunStr.GetLabels()
|
return r.kunStr.GetLabels()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) GetMap(p string) (map[string]interface{}, error) {
|
|
||||||
return r.kunStr.GetMap(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) GetName() string {
|
func (r *Resource) GetName() string {
|
||||||
return r.kunStr.GetName()
|
return r.kunStr.GetName()
|
||||||
}
|
}
|
||||||
@@ -130,14 +68,6 @@ func (r *Resource) GetString(p string) (string, error) {
|
|||||||
return r.kunStr.GetString(p)
|
return r.kunStr.GetString(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) GetStringMap(p string) (map[string]string, error) {
|
|
||||||
return r.kunStr.GetStringMap(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) GetStringSlice(p string) ([]string, error) {
|
|
||||||
return r.kunStr.GetStringSlice(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) Map() map[string]interface{} {
|
func (r *Resource) Map() map[string]interface{} {
|
||||||
return r.kunStr.Map()
|
return r.kunStr.Map()
|
||||||
}
|
}
|
||||||
@@ -154,10 +84,6 @@ func (r *Resource) MatchesAnnotationSelector(selector string) (bool, error) {
|
|||||||
return r.kunStr.MatchesAnnotationSelector(selector)
|
return r.kunStr.MatchesAnnotationSelector(selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) Patch(other ifc.Kunstructured) error {
|
|
||||||
return r.kunStr.Patch(other)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) SetAnnotations(m map[string]string) {
|
func (r *Resource) SetAnnotations(m map[string]string) {
|
||||||
r.kunStr.SetAnnotations(m)
|
r.kunStr.SetAnnotations(m)
|
||||||
}
|
}
|
||||||
@@ -170,10 +96,6 @@ func (r *Resource) SetLabels(m map[string]string) {
|
|||||||
r.kunStr.SetLabels(m)
|
r.kunStr.SetLabels(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) SetMap(m map[string]interface{}) {
|
|
||||||
r.kunStr.SetMap(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resource) SetName(n string) {
|
func (r *Resource) SetName(n string) {
|
||||||
r.kunStr.SetName(n)
|
r.kunStr.SetName(n)
|
||||||
}
|
}
|
||||||
@@ -205,7 +127,7 @@ type ResCtxMatcher func(ResCtx) bool
|
|||||||
// DeepCopy returns a new copy of resource
|
// DeepCopy returns a new copy of resource
|
||||||
func (r *Resource) DeepCopy() *Resource {
|
func (r *Resource) DeepCopy() *Resource {
|
||||||
rc := &Resource{
|
rc := &Resource{
|
||||||
kunStr: r.kunStr.Copy(),
|
kunStr: r.Copy(),
|
||||||
}
|
}
|
||||||
rc.copyOtherFields(r)
|
rc.copyOtherFields(r)
|
||||||
return rc
|
return rc
|
||||||
@@ -213,11 +135,10 @@ func (r *Resource) DeepCopy() *Resource {
|
|||||||
|
|
||||||
// Replace performs replace with other resource.
|
// Replace performs replace with other resource.
|
||||||
func (r *Resource) Replace(other *Resource) {
|
func (r *Resource) Replace(other *Resource) {
|
||||||
r.kunStr.SetLabels(mergeStringMaps(other.kunStr.GetLabels(), r.kunStr.GetLabels()))
|
r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels()))
|
||||||
r.kunStr.SetAnnotations(
|
r.SetAnnotations(mergeStringMaps(other.GetAnnotations(), r.GetAnnotations()))
|
||||||
mergeStringMaps(other.kunStr.GetAnnotations(), r.kunStr.GetAnnotations()))
|
r.SetName(other.GetName())
|
||||||
r.kunStr.SetName(other.GetName())
|
r.SetNamespace(other.GetNamespace())
|
||||||
r.kunStr.SetNamespace(other.GetNamespace())
|
|
||||||
r.copyOtherFields(other)
|
r.copyOtherFields(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,7 +179,7 @@ func (r *Resource) KunstructEqual(o *Resource) bool {
|
|||||||
// Merge performs merge with other resource.
|
// Merge performs merge with other resource.
|
||||||
func (r *Resource) Merge(other *Resource) {
|
func (r *Resource) Merge(other *Resource) {
|
||||||
r.Replace(other)
|
r.Replace(other)
|
||||||
mergeConfigmap(r.kunStr.Map(), other.Map(), r.Map())
|
mergeConfigmap(r.Map(), other.Map(), r.Map())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) copyRefBy() []resid.ResId {
|
func (r *Resource) copyRefBy() []resid.ResId {
|
||||||
@@ -356,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
|
||||||
}
|
}
|
||||||
@@ -384,7 +299,7 @@ func (r *Resource) setOriginalNs(n string) *Resource {
|
|||||||
|
|
||||||
// String returns resource as JSON.
|
// String returns resource as JSON.
|
||||||
func (r *Resource) String() string {
|
func (r *Resource) String() string {
|
||||||
bs, err := r.kunStr.MarshalJSON()
|
bs, err := r.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "<" + err.Error() + ">"
|
return "<" + err.Error() + ">"
|
||||||
}
|
}
|
||||||
@@ -394,7 +309,7 @@ func (r *Resource) String() string {
|
|||||||
// AsYAML returns the resource in Yaml form.
|
// AsYAML returns the resource in Yaml form.
|
||||||
// Easier to read than JSON.
|
// Easier to read than JSON.
|
||||||
func (r *Resource) AsYAML() ([]byte, error) {
|
func (r *Resource) AsYAML() ([]byte, error) {
|
||||||
json, err := r.kunStr.MarshalJSON()
|
json, err := r.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -419,7 +334,7 @@ func (r *Resource) NeedHashSuffix() bool {
|
|||||||
|
|
||||||
// GetNamespace returns the namespace the resource thinks it's in.
|
// GetNamespace returns the namespace the resource thinks it's in.
|
||||||
func (r *Resource) GetNamespace() string {
|
func (r *Resource) GetNamespace() string {
|
||||||
namespace, _ := r.kunStr.GetString("metadata.namespace")
|
namespace, _ := r.GetString("metadata.namespace")
|
||||||
// if err, namespace is empty, so no need to check.
|
// if err, namespace is empty, so no need to check.
|
||||||
return namespace
|
return namespace
|
||||||
}
|
}
|
||||||
@@ -429,7 +344,7 @@ func (r *Resource) GetNamespace() string {
|
|||||||
// TODO: compute this once and save it in the resource.
|
// TODO: compute this once and save it in the resource.
|
||||||
func (r *Resource) OrgId() resid.ResId {
|
func (r *Resource) OrgId() resid.ResId {
|
||||||
return resid.NewResIdWithNamespace(
|
return resid.NewResIdWithNamespace(
|
||||||
r.kunStr.GetGvk(), r.GetOriginalName(), r.GetOriginalNs())
|
r.GetGvk(), r.GetOriginalName(), r.GetOriginalNs())
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurId returns a ResId for the resource using the
|
// CurId returns a ResId for the resource using the
|
||||||
@@ -437,7 +352,7 @@ func (r *Resource) OrgId() resid.ResId {
|
|||||||
// This should be unique in any ResMap.
|
// This should be unique in any ResMap.
|
||||||
func (r *Resource) CurId() resid.ResId {
|
func (r *Resource) CurId() resid.ResId {
|
||||||
return resid.NewResIdWithNamespace(
|
return resid.NewResIdWithNamespace(
|
||||||
r.kunStr.GetGvk(), r.kunStr.GetName(), r.GetNamespace())
|
r.GetGvk(), r.GetName(), r.GetNamespace())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRefBy returns the ResIds that referred to current resource
|
// GetRefBy returns the ResIds that referred to current resource
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/filesys"
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer"
|
"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/k8sdeps/kunstruct"
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
"sigs.k8s.io/kustomize/api/konfig"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
@@ -46,16 +46,17 @@ func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
resourceFactory := resource.NewFactory(
|
||||||
rf := resmap.NewFactory(
|
kunstruct.NewKunstructuredFactoryImpl())
|
||||||
resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()),
|
resmapFactory := resmap.NewFactory(
|
||||||
transformer.NewFactoryImpl())
|
resourceFactory,
|
||||||
|
merge.NewMerginator(resourceFactory))
|
||||||
|
|
||||||
result := &HarnessEnhanced{
|
result := &HarnessEnhanced{
|
||||||
Harness: MakeHarness(t),
|
Harness: MakeHarness(t),
|
||||||
pte: pte,
|
pte: pte,
|
||||||
rf: rf,
|
rf: resmapFactory,
|
||||||
pl: pLdr.NewLoader(pc, rf)}
|
pl: pLdr.NewLoader(pc, resmapFactory)}
|
||||||
|
|
||||||
// Point the file loader to the root ('/') of the in-memory file system.
|
// Point the file loader to the root ('/') of the in-memory file system.
|
||||||
result.ResetLoaderRoot(filesys.Separator)
|
result.ResetLoaderRoot(filesys.Separator)
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package transform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mutateFunc func(interface{}) (interface{}, error)
|
|
||||||
|
|
||||||
func MutateField(
|
|
||||||
m map[string]interface{},
|
|
||||||
pathToField []string,
|
|
||||||
createIfNotPresent bool,
|
|
||||||
fns ...mutateFunc) error {
|
|
||||||
if len(pathToField) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
firstPathSegment, isArray := getFirstPathSegment(pathToField)
|
|
||||||
|
|
||||||
_, found := m[firstPathSegment]
|
|
||||||
if !found {
|
|
||||||
if !createIfNotPresent || isArray {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
m[firstPathSegment] = map[string]interface{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pathToField) == 1 {
|
|
||||||
var err error
|
|
||||||
for _, fn := range fns {
|
|
||||||
m[firstPathSegment], err = fn(m[firstPathSegment])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
v := m[firstPathSegment]
|
|
||||||
newPathToField := pathToField[1:]
|
|
||||||
switch typedV := v.(type) {
|
|
||||||
case nil:
|
|
||||||
log.Printf(
|
|
||||||
"nil value at `%s` ignored in mutation attempt",
|
|
||||||
strings.Join(pathToField, "."))
|
|
||||||
return nil
|
|
||||||
case map[string]interface{}:
|
|
||||||
return MutateField(typedV, newPathToField, createIfNotPresent, fns...)
|
|
||||||
case []interface{}:
|
|
||||||
for i := range typedV {
|
|
||||||
item := typedV[i]
|
|
||||||
typedItem, ok := item.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%#v is expected to be %T", item, typedItem)
|
|
||||||
}
|
|
||||||
err := MutateField(typedItem, newPathToField, createIfNotPresent, fns...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("%#v is not expected to be a primitive type", typedV)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFirstPathSegment(pathToField []string) (string, bool) {
|
|
||||||
if strings.HasSuffix(pathToField[0], "[]") {
|
|
||||||
return pathToField[0][:len(pathToField[0])-2], true
|
|
||||||
}
|
|
||||||
return pathToField[0], false
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
// Copyright 2019 The Kubernetes Authors.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package transform_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
|
||||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
|
||||||
. "sigs.k8s.io/kustomize/api/transform"
|
|
||||||
)
|
|
||||||
|
|
||||||
type noopMutator struct {
|
|
||||||
wasCalled bool
|
|
||||||
errorToReturn error
|
|
||||||
}
|
|
||||||
|
|
||||||
var errExpected = fmt.Errorf("oops")
|
|
||||||
|
|
||||||
const originalValue = "tomato"
|
|
||||||
const newValue = "notThe" + originalValue
|
|
||||||
|
|
||||||
func (m *noopMutator) mutate(_ interface{}) (interface{}, error) {
|
|
||||||
m.wasCalled = true
|
|
||||||
return newValue, m.errorToReturn
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeTestDeployment() ifc.Kunstructured {
|
|
||||||
factory := kunstruct.NewKunstructuredFactoryImpl()
|
|
||||||
return factory.FromMap(
|
|
||||||
map[string]interface{}{
|
|
||||||
"group": "apps",
|
|
||||||
"apiVersion": "v1",
|
|
||||||
"kind": "Deployment",
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"name": originalValue,
|
|
||||||
},
|
|
||||||
"spec": map[string]interface{}{
|
|
||||||
"template": map[string]interface{}{
|
|
||||||
"env": []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"name": "HELLO",
|
|
||||||
"value": "hi there",
|
|
||||||
},
|
|
||||||
map[string]interface{}{
|
|
||||||
"name": "GOODBYE",
|
|
||||||
"value": "adios!",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"metadata": map[string]interface{}{
|
|
||||||
"labels": map[string]interface{}{
|
|
||||||
"vegetable": originalValue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"spec": map[string]interface{}{
|
|
||||||
"containers": []interface{}{
|
|
||||||
map[string]interface{}{
|
|
||||||
"name": "tangerine",
|
|
||||||
"image": originalValue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFieldValue(t *testing.T, obj ifc.Kunstructured, fieldName string) string {
|
|
||||||
v, err := obj.GetString(fieldName)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected field error: %v", err)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoPath(t *testing.T) {
|
|
||||||
obj := makeTestDeployment()
|
|
||||||
m := &noopMutator{}
|
|
||||||
err := MutateField(
|
|
||||||
obj.Map(), []string{}, false, m.mutate)
|
|
||||||
if m.wasCalled {
|
|
||||||
t.Fatalf("mutator should not have been called.")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHappyPath(t *testing.T) {
|
|
||||||
obj := makeTestDeployment()
|
|
||||||
v := getFieldValue(t, obj, "metadata.name")
|
|
||||||
if v != originalValue {
|
|
||||||
t.Fatalf("unexpected original value: %v", v)
|
|
||||||
}
|
|
||||||
v = getFieldValue(t, obj, "spec.template.metadata.labels.vegetable")
|
|
||||||
if v != originalValue {
|
|
||||||
t.Fatalf("unexpected original value: %v", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
m := &noopMutator{}
|
|
||||||
err := MutateField(
|
|
||||||
obj.Map(), []string{"metadata", "name"}, false, m.mutate)
|
|
||||||
if !m.wasCalled {
|
|
||||||
t.Fatalf("mutator should have been called.")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected mutate error: %v", err)
|
|
||||||
}
|
|
||||||
v = getFieldValue(t, obj, "metadata.name")
|
|
||||||
if v != newValue {
|
|
||||||
t.Fatalf("unexpected new value: %v", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
m = &noopMutator{}
|
|
||||||
err = MutateField(
|
|
||||||
obj.Map(), []string{"spec", "template", "metadata", "labels", "vegetable"}, false, m.mutate)
|
|
||||||
if !m.wasCalled {
|
|
||||||
t.Fatalf("mutator should have been called.")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected mutate error: %v", err)
|
|
||||||
}
|
|
||||||
v = getFieldValue(t, obj, "spec.template.metadata.labels.vegetable")
|
|
||||||
if v != newValue {
|
|
||||||
t.Fatalf("unexpected new value: %v", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithError(t *testing.T) {
|
|
||||||
obj := makeTestDeployment()
|
|
||||||
m := noopMutator{errorToReturn: errExpected}
|
|
||||||
err := MutateField(
|
|
||||||
obj.Map(), []string{"metadata", "name"}, false, m.mutate)
|
|
||||||
if !m.wasCalled {
|
|
||||||
t.Fatalf("mutator was not called!")
|
|
||||||
}
|
|
||||||
if err != errExpected {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithNil(t *testing.T) {
|
|
||||||
obj := makeTestDeployment()
|
|
||||||
foo := obj.Map()["spec"]
|
|
||||||
foo = foo.(map[string]interface{})["template"]
|
|
||||||
foo = foo.(map[string]interface{})["metadata"]
|
|
||||||
foo.(map[string]interface{})["labels"] = nil
|
|
||||||
|
|
||||||
m := &noopMutator{}
|
|
||||||
err := MutateField(
|
|
||||||
obj.Map(), []string{"spec", "template", "metadata", "labels", "vegetable"}, false, m.mutate)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,14 +9,14 @@ import (
|
|||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FixKustomizationPreUnmarshalling modies the raw data
|
// FixKustomizationPreUnmarshalling modifies the raw data
|
||||||
// before marshalling - e.g. changes old field names to
|
// before marshalling - e.g. changes old field names to
|
||||||
// new field names.
|
// new field names.
|
||||||
func FixKustomizationPreUnmarshalling(data []byte) ([]byte, error) {
|
func FixKustomizationPreUnmarshalling(data []byte) ([]byte, error) {
|
||||||
deprecateFieldsMap := map[string]string{
|
deprecatedFieldsMap := map[string]string{
|
||||||
"imageTags:": "images:",
|
"imageTags:": "images:",
|
||||||
}
|
}
|
||||||
for oldname, newname := range deprecateFieldsMap {
|
for oldname, newname := range deprecatedFieldsMap {
|
||||||
pattern := regexp.MustCompile(oldname)
|
pattern := regexp.MustCompile(oldname)
|
||||||
data = pattern.ReplaceAll(data, []byte(newname))
|
data = pattern.ReplaceAll(data, []byte(newname))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ type Kustomization struct {
|
|||||||
// moving content of deprecated fields to newer
|
// moving content of deprecated fields to newer
|
||||||
// fields.
|
// fields.
|
||||||
func (k *Kustomization) FixKustomizationPostUnmarshalling() {
|
func (k *Kustomization) FixKustomizationPostUnmarshalling() {
|
||||||
|
|
||||||
if k.Kind == "" {
|
if k.Kind == "" {
|
||||||
k.Kind = KustomizationKind
|
k.Kind = KustomizationKind
|
||||||
}
|
}
|
||||||
@@ -158,6 +159,17 @@ func (k *Kustomization) FixKustomizationPostUnmarshalling() {
|
|||||||
k.Bases = nil
|
k.Bases = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FixKustomizationPreMarshalling fixes things
|
||||||
|
// that should occur after the kustomization file
|
||||||
|
// has been processed.
|
||||||
|
func (k *Kustomization) FixKustomizationPreMarshalling() {
|
||||||
|
// PatchesJson6902 should be under the Patches field.
|
||||||
|
for _, patch := range k.PatchesJson6902 {
|
||||||
|
k.Patches = append(k.Patches, patch.ToPatch())
|
||||||
|
}
|
||||||
|
k.PatchesJson6902 = nil
|
||||||
|
}
|
||||||
|
|
||||||
func (k *Kustomization) EnforceFields() []string {
|
func (k *Kustomization) EnforceFields() []string {
|
||||||
var errs []string
|
var errs []string
|
||||||
if k.Kind != "" && k.Kind != KustomizationKind && k.Kind != ComponentKind {
|
if k.Kind != "" && k.Kind != KustomizationKind && k.Kind != ComponentKind {
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func fixKustomizationPostUnmarshallingCheck(k, e *Kustomization) bool {
|
func fixKustomizationPostUnmarshallingCheck(k, e *Kustomization) bool {
|
||||||
return (k.Kind == e.Kind && k.APIVersion == e.APIVersion &&
|
return k.Kind == e.Kind &&
|
||||||
len(k.Resources) == len(e.Resources) && k.Resources[0] == e.Resources[0] &&
|
k.APIVersion == e.APIVersion &&
|
||||||
k.Bases == nil)
|
len(k.Resources) == len(e.Resources) &&
|
||||||
|
k.Resources[0] == e.Resources[0] &&
|
||||||
|
k.Bases == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFixKustomizationPostUnmarshalling(t *testing.T) {
|
func TestFixKustomizationPostUnmarshalling(t *testing.T) {
|
||||||
|
|||||||
@@ -19,3 +19,9 @@ type PatchJson6902 struct {
|
|||||||
// inline patch string
|
// inline patch string
|
||||||
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToPatch converts a PatchJson6902 to its superset Patch.
|
||||||
|
func (patch *PatchJson6902) ToPatch() Patch {
|
||||||
|
selector := patch.Target.ToSelector()
|
||||||
|
return Patch{Path: patch.Path, Patch: patch.Patch, Target: &selector}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,3 +13,8 @@ type PatchTarget struct {
|
|||||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||||
Name string `json:"name" yaml:"name"`
|
Name string `json:"name" yaml:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToSelector converts a PatchTarget to a Selector.
|
||||||
|
func (target *PatchTarget) ToSelector() Selector {
|
||||||
|
return Selector{Name: target.Name, Namespace: target.Namespace, Gvk: target.Gvk}
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ var (
|
|||||||
CreateSetter = commands.CreateSetterCommand
|
CreateSetter = commands.CreateSetterCommand
|
||||||
CreateSubstitution = commands.CreateSubstitutionCommand
|
CreateSubstitution = commands.CreateSubstitutionCommand
|
||||||
DeleteSetter = commands.DeleteSetterCommand
|
DeleteSetter = commands.DeleteSetterCommand
|
||||||
|
DeleteSubstitution = commands.DeleteSubstitutionCommand
|
||||||
Fmt = commands.FmtCommand
|
Fmt = commands.FmtCommand
|
||||||
Grep = commands.GrepCommand
|
Grep = commands.GrepCommand
|
||||||
Init = commands.InitCommand
|
Init = commands.InitCommand
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ func GetLive(name string) *cobra.Command {
|
|||||||
cmd.AddCommand(
|
cmd.AddCommand(
|
||||||
applyCmd,
|
applyCmd,
|
||||||
initcmd.NewCmdInit(ioStreams),
|
initcmd.NewCmdInit(ioStreams),
|
||||||
preview.NewCmdPreview(f, ioStreams),
|
preview.GetPreviewRunner(f, ioStreams).Command,
|
||||||
diff.NewCmdDiff(f, ioStreams),
|
diff.NewCmdDiff(f, ioStreams),
|
||||||
destroy.NewCmdDestroy(f, ioStreams))
|
destroy.GetDestroyRunner(f, ioStreams).Command)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,6 +14,8 @@ 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.17.0
|
sigs.k8s.io/cli-utils v0.19.0
|
||||||
sigs.k8s.io/kustomize/kyaml v0.5.0
|
sigs.k8s.io/kustomize/kyaml v0.6.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml
|
||||||
|
|||||||
@@ -369,6 +369,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=
|
||||||
@@ -454,8 +455,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
|
||||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||||
@@ -555,7 +554,6 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
|||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||||
@@ -617,15 +615,12 @@ 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.17.0 h1:iQ0nhgU8DZiRphHTErI1IHcHYp2fZaULrrFN4NF3dCc=
|
sigs.k8s.io/cli-utils v0.19.0 h1:lAoR5okhSV/dIusodaQp5VbDpHMcKnvjqKYHU+AB3a4=
|
||||||
sigs.k8s.io/cli-utils v0.17.0/go.mod h1:9Jqm9K2W6ShhCxsEuaz6HSRKKOXigPUx3ZfypGgxBLY=
|
sigs.k8s.io/cli-utils v0.19.0/go.mod h1:B7KdqkSkHNIUn3cFbaR4aKUZMKtr+Benboi1w/HW/Fg=
|
||||||
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=
|
||||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||||
sigs.k8s.io/kustomize/kyaml v0.4.0/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
|
|
||||||
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/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
|
|||||||
set.Flags().StringVar(&r.Set.SetPartialField.Description, "description", "",
|
set.Flags().StringVar(&r.Set.SetPartialField.Description, "description", "",
|
||||||
"record a description for the current setter value.")
|
"record a description for the current setter value.")
|
||||||
set.Flags().StringVar(&r.Set.SetPartialField.Field, "field", "",
|
set.Flags().StringVar(&r.Set.SetPartialField.Field, "field", "",
|
||||||
"name of the field to set -- e.g. --field port. defaults to all fields match"+
|
"name of the field to set, a suffix of the path to the field, or the full"+
|
||||||
"VALUE. maybe be the field name, field path, or partial field path (suffix)")
|
" path to the field. Default is to match all fields.")
|
||||||
set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "",
|
set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "",
|
||||||
"name of the Resource on which to create the setter.")
|
"name of the Resource on which to create the setter.")
|
||||||
set.Flags().MarkHidden("name")
|
set.Flags().MarkHidden("name")
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"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/openapi"
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||||
)
|
)
|
||||||
@@ -16,7 +17,7 @@ func NewDeleteSetterRunner(parent string) *DeleteSetterRunner {
|
|||||||
r := &DeleteSetterRunner{}
|
r := &DeleteSetterRunner{}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "delete-setter DIR NAME",
|
Use: "delete-setter DIR NAME",
|
||||||
Args: cobra.MinimumNArgs(2),
|
Args: cobra.ExactArgs(2),
|
||||||
Short: commands.DeleteSetterShort,
|
Short: commands.DeleteSetterShort,
|
||||||
Long: commands.DeleteSetterLong,
|
Long: commands.DeleteSetterLong,
|
||||||
Example: commands.DeleteSetterExamples,
|
Example: commands.DeleteSetterExamples,
|
||||||
@@ -42,6 +43,7 @@ type DeleteSetterRunner struct {
|
|||||||
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
r.DeleteSetter.Name = args[1]
|
r.DeleteSetter.Name = args[1]
|
||||||
|
r.DeleteSetter.DefinitionPrefix = fieldmeta.SetterDefinitionPrefix
|
||||||
|
|
||||||
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -30,25 +30,25 @@ func TestDeleteSetterCommand(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "delete replicas",
|
name: "delete replicas",
|
||||||
args: []string{"replicas", "hello world"},
|
args: []string{"replicas-setter"},
|
||||||
input: `
|
input: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
spec:
|
spec:
|
||||||
replicas: 3 # {"$openapi" : "replicas"}}
|
replicas: 3 # {"$openapi" : "replicas-setter"}
|
||||||
`,
|
`,
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
openAPI:
|
openAPI:
|
||||||
definitions:
|
definitions:
|
||||||
io.k8s.cli.setters.replicas:
|
io.k8s.cli.setters.replicas-setter:
|
||||||
description: hello world
|
description: hello world
|
||||||
x-k8s-cli:
|
x-k8s-cli:
|
||||||
setter:
|
setter:
|
||||||
name: replicas
|
name: replicas-setter
|
||||||
value: "3"
|
value: "3"
|
||||||
setBy: me
|
setBy: me
|
||||||
`,
|
`,
|
||||||
@@ -67,32 +67,33 @@ spec:
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete only one setter",
|
name: "delete only one setter",
|
||||||
args: []string{"replicas", "hello world"},
|
args: []string{"replicas-setter"},
|
||||||
input: `
|
input: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
spec:
|
spec:
|
||||||
replicas: 3 # {"$openapi" : "replicas"}}
|
replicas: 3 # {"$openapi" : "replicas-setter"}
|
||||||
|
foo: nginx # {"$openapi" : "image"}
|
||||||
`,
|
`,
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
openAPI:
|
openAPI:
|
||||||
definitions:
|
definitions:
|
||||||
io.k8s.cli.setters.replicas:
|
io.k8s.cli.setters.replicas-setter:
|
||||||
description: hello world
|
description: hello world
|
||||||
x-k8s-cli:
|
x-k8s-cli:
|
||||||
setter:
|
setter:
|
||||||
name: replicas
|
name: replicas-setter
|
||||||
value: "3"
|
value: "3"
|
||||||
setBy: me
|
setBy: me
|
||||||
io.k8s.cli.setters.image:
|
io.k8s.cli.setters.image:
|
||||||
x-k8s-cli:
|
x-k8s-cli:
|
||||||
setter:
|
setter:
|
||||||
name: image
|
name: image
|
||||||
value: 1.0
|
value: nginx
|
||||||
`,
|
`,
|
||||||
expectedOpenAPI: `
|
expectedOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
@@ -103,7 +104,7 @@ openAPI:
|
|||||||
x-k8s-cli:
|
x-k8s-cli:
|
||||||
setter:
|
setter:
|
||||||
name: image
|
name: image
|
||||||
value: 1.0
|
value: nginx
|
||||||
`,
|
`,
|
||||||
expectedResources: `
|
expectedResources: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
@@ -112,29 +113,95 @@ metadata:
|
|||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
|
foo: nginx # {"$openapi" : "image"}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "delete array setter",
|
||||||
|
args: []string{"list"},
|
||||||
|
inputOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.list:
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
maxItems: 3
|
||||||
|
type: array
|
||||||
|
description: hello world
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: list
|
||||||
|
value: ""
|
||||||
|
listValues:
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
- c
|
||||||
|
setBy: me
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: example.com/v1beta1
|
||||||
|
kind: Example1
|
||||||
|
spec:
|
||||||
|
list: # {"$openapi":"list"}
|
||||||
|
- "a"
|
||||||
|
- "b"
|
||||||
|
- "c"
|
||||||
|
---
|
||||||
|
apiVersion: example.com/v1beta1
|
||||||
|
kind: Example2
|
||||||
|
spec:
|
||||||
|
list: # {"$openapi":"list2"}
|
||||||
|
- "a"
|
||||||
|
- "b"
|
||||||
|
- "c"
|
||||||
|
`,
|
||||||
|
expectedResources: `
|
||||||
|
apiVersion: example.com/v1beta1
|
||||||
|
kind: Example1
|
||||||
|
spec:
|
||||||
|
list:
|
||||||
|
- "a"
|
||||||
|
- "b"
|
||||||
|
- "c"
|
||||||
|
---
|
||||||
|
apiVersion: example.com/v1beta1
|
||||||
|
kind: Example2
|
||||||
|
spec:
|
||||||
|
list: # {"$openapi":"list2"}
|
||||||
|
- "a"
|
||||||
|
- "b"
|
||||||
|
- "c"
|
||||||
|
`,
|
||||||
|
expectedOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "delete non exist setter error",
|
name: "delete non exist setter error",
|
||||||
args: []string{"image", "hello world"},
|
args: []string{"image"},
|
||||||
input: `
|
input: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
spec:
|
spec:
|
||||||
replicas: 3 # {"$openapi" : "replicas"}}
|
replicas: 3 # {"$openapi" : "replicas-setter"}
|
||||||
`,
|
`,
|
||||||
inputOpenAPI: `
|
inputOpenAPI: `
|
||||||
apiVersion: v1alpha1
|
apiVersion: v1alpha1
|
||||||
kind: Example
|
kind: Example
|
||||||
openAPI:
|
openAPI:
|
||||||
definitions:
|
definitions:
|
||||||
io.k8s.cli.setters.replicas:
|
io.k8s.cli.setters.replicas-setter:
|
||||||
description: hello world
|
description: hello world
|
||||||
x-k8s-cli:
|
x-k8s-cli:
|
||||||
setter:
|
setter:
|
||||||
name: replicas
|
name: replicas-setter
|
||||||
value: "3"
|
value: "3"
|
||||||
setBy: me
|
setBy: me
|
||||||
`,
|
`,
|
||||||
@@ -143,11 +210,11 @@ apiVersion: v1alpha1
|
|||||||
kind: Example
|
kind: Example
|
||||||
openAPI:
|
openAPI:
|
||||||
definitions:
|
definitions:
|
||||||
io.k8s.cli.setters.replicas:
|
io.k8s.cli.setters.replicas-setter:
|
||||||
description: hello world
|
description: hello world
|
||||||
x-k8s-cli:
|
x-k8s-cli:
|
||||||
setter:
|
setter:
|
||||||
name: replicas
|
name: replicas-setter
|
||||||
value: "3"
|
value: "3"
|
||||||
setBy: me
|
setBy: me
|
||||||
`,
|
`,
|
||||||
@@ -157,13 +224,13 @@ kind: Deployment
|
|||||||
metadata:
|
metadata:
|
||||||
name: nginx-deployment
|
name: nginx-deployment
|
||||||
spec:
|
spec:
|
||||||
replicas: 3 # {"$openapi" : "replicas"}}
|
replicas: 3 # {"$openapi" : "replicas-setter"}
|
||||||
`,
|
`,
|
||||||
err: `setter does not exist`,
|
err: `setter with name image does not exist`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete setter used in substitution error",
|
name: "delete setter used in substitution error",
|
||||||
args: []string{"image-name", "hello world"},
|
args: []string{"image-name"},
|
||||||
input: `
|
input: `
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
@@ -220,7 +287,7 @@ openAPI:
|
|||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
`,
|
`,
|
||||||
err: `setter is used in substitution image, please delete the substitution first`,
|
err: `setter is used in substitution image, please delete the parent substitution first`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i := range tests {
|
for i := range tests {
|
||||||
|
|||||||
62
cmd/config/internal/commands/cmddeletesubstitution.go
Normal file
62
cmd/config/internal/commands/cmddeletesubstitution.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDeleteRunner returns a command runner.
|
||||||
|
func NewDeleteSubstitutionRunner(parent string) *DeleteSubstitutionRunner {
|
||||||
|
r := &DeleteSubstitutionRunner{}
|
||||||
|
c := &cobra.Command{
|
||||||
|
Use: "delete-subst DIR NAME",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
PreRunE: r.preRunE,
|
||||||
|
RunE: r.runE,
|
||||||
|
}
|
||||||
|
fixDocs(parent, c)
|
||||||
|
r.Command = c
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSubstitutionCommand(parent string) *cobra.Command {
|
||||||
|
return NewDeleteSubstitutionRunner(parent).Command
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeleteSubstitutionRunner struct {
|
||||||
|
Command *cobra.Command
|
||||||
|
DeleteSubstitution settersutil.DeleterCreator
|
||||||
|
OpenAPIFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
|
||||||
|
var err error
|
||||||
|
r.DeleteSubstitution.Name = args[1]
|
||||||
|
r.DeleteSubstitution.DefinitionPrefix = fieldmeta.SubstitutionDefinitionPrefix
|
||||||
|
|
||||||
|
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DeleteSubstitutionRunner) runE(c *cobra.Command, args []string) error {
|
||||||
|
return handleError(c, r.delete(c, args))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *DeleteSubstitutionRunner) delete(c *cobra.Command, args []string) error {
|
||||||
|
return r.DeleteSubstitution.Delete(r.OpenAPIFile, args[0])
|
||||||
|
}
|
||||||
497
cmd/config/internal/commands/cmddeletesubstitution_test.go
Normal file
497
cmd/config/internal/commands/cmddeletesubstitution_test.go
Normal file
@@ -0,0 +1,497 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package commands_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/ext"
|
||||||
|
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeleteSubstitutionCommand(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
args []string
|
||||||
|
schema string
|
||||||
|
out string
|
||||||
|
inputOpenAPI string
|
||||||
|
expectedOpenAPI string
|
||||||
|
expectedResources string
|
||||||
|
err string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "delete only subst if setter has same name - long ref",
|
||||||
|
args: []string{"my.image"},
|
||||||
|
inputOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my.image:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my.image
|
||||||
|
value: "nginx"
|
||||||
|
io.k8s.cli.setters.my-tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag
|
||||||
|
value: "1.7.9"
|
||||||
|
io.k8s.cli.substitutions.my.image:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: my.image
|
||||||
|
pattern: ${my.image}:${my-tag}
|
||||||
|
values:
|
||||||
|
- marker: ${my.image}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my.image'
|
||||||
|
- marker: ${my-tag}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-tag'
|
||||||
|
io.k8s.cli.setters.replicas:
|
||||||
|
description: hello world
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: replicas
|
||||||
|
value: "3"
|
||||||
|
setBy: me
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3 # {"$openapi":"replicas"}
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.my.image"}
|
||||||
|
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"}
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"}
|
||||||
|
`,
|
||||||
|
expectedResources: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3 # {"$openapi":"replicas"}
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx # {"$ref":"#/definitions/io.k8s.cli.setters.my.image"}
|
||||||
|
image: nginx:1.7.9
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx:1.7.9
|
||||||
|
`,
|
||||||
|
expectedOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my.image:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my.image
|
||||||
|
value: "nginx"
|
||||||
|
io.k8s.cli.setters.my-tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag
|
||||||
|
value: "1.7.9"
|
||||||
|
io.k8s.cli.setters.replicas:
|
||||||
|
description: hello world
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: replicas
|
||||||
|
value: "3"
|
||||||
|
setBy: me
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "delete subst - short ref",
|
||||||
|
args: []string{"my-image-sub"},
|
||||||
|
inputOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my-image:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-image
|
||||||
|
value: "nginx"
|
||||||
|
io.k8s.cli.setters.my-tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag
|
||||||
|
value: "1.7.9"
|
||||||
|
io.k8s.cli.substitutions.my-image-sub:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: my-image-sub
|
||||||
|
pattern: ${my-image}:${my-tag}
|
||||||
|
values:
|
||||||
|
- marker: ${my-image}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-image'
|
||||||
|
- marker: ${my-tag}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-tag'
|
||||||
|
io.k8s.cli.setters.replicas:
|
||||||
|
description: hello world
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: replicas
|
||||||
|
value: "3"
|
||||||
|
setBy: me
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3 # {"$openapi":"replicas"}
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
||||||
|
`,
|
||||||
|
expectedResources: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3 # {"$openapi":"replicas"}
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.7.9
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx:1.7.9
|
||||||
|
`,
|
||||||
|
expectedOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my-image:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-image
|
||||||
|
value: "nginx"
|
||||||
|
io.k8s.cli.setters.my-tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag
|
||||||
|
value: "1.7.9"
|
||||||
|
io.k8s.cli.setters.replicas:
|
||||||
|
description: hello world
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: replicas
|
||||||
|
value: "3"
|
||||||
|
setBy: me
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "error if subst doesn't exist",
|
||||||
|
args: []string{"my-image-sub-not-present"},
|
||||||
|
inputOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my-image:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-image
|
||||||
|
value: "nginx"
|
||||||
|
io.k8s.cli.setters.my-tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag
|
||||||
|
value: "1.7.9"
|
||||||
|
io.k8s.cli.substitutions.my-image-sub:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: my-image-sub
|
||||||
|
pattern: ${my-image}:${my-tag}
|
||||||
|
values:
|
||||||
|
- marker: ${my-image}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-image'
|
||||||
|
- marker: ${my-tag}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-tag'
|
||||||
|
io.k8s.cli.setters.replicas:
|
||||||
|
description: hello world
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: replicas
|
||||||
|
value: "3"
|
||||||
|
setBy: me
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3 # {"$openapi":"replicas"}
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
|
||||||
|
`,
|
||||||
|
expectedResources: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3 # {"$openapi":"replicas"}
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.7.9
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx:1.7.9
|
||||||
|
`,
|
||||||
|
expectedOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my-image:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-image
|
||||||
|
value: "nginx"
|
||||||
|
io.k8s.cli.setters.my-tag:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag
|
||||||
|
value: "1.7.9"
|
||||||
|
io.k8s.cli.setters.replicas:
|
||||||
|
description: hello world
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: replicas
|
||||||
|
value: "3"
|
||||||
|
setBy: me
|
||||||
|
`,
|
||||||
|
err: "substitution with name my-image-sub-not-present does not exist",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "substitution referenced by other substitution",
|
||||||
|
args: []string{"my-image-subst"},
|
||||||
|
inputOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my-image-setter:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-image-setter
|
||||||
|
value: nginx
|
||||||
|
io.k8s.cli.setters.my-tag-setter:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag-setter
|
||||||
|
value: 1.7.9
|
||||||
|
io.k8s.cli.substitutions.my-image-subst:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: my-image-subst
|
||||||
|
pattern: ${my-image-setter}::${my-tag-setter}
|
||||||
|
values:
|
||||||
|
- marker: ${my-image-setter}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
||||||
|
- marker: ${my-tag-setter}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||||
|
io.k8s.cli.substitutions.my-nested-subst:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: my-nested-subst
|
||||||
|
pattern: something/${my-image-subst}/${my-other-setter}
|
||||||
|
values:
|
||||||
|
- marker: ${my-image-subst}
|
||||||
|
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
||||||
|
- marker: ${my-other-setter}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
||||||
|
io.k8s.cli.setters.my-other-setter:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-other-setter
|
||||||
|
value: nginxotherthing
|
||||||
|
`,
|
||||||
|
input: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
||||||
|
`,
|
||||||
|
expectedOpenAPI: `
|
||||||
|
apiVersion: v1alpha1
|
||||||
|
kind: Example
|
||||||
|
openAPI:
|
||||||
|
definitions:
|
||||||
|
io.k8s.cli.setters.my-image-setter:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-image-setter
|
||||||
|
value: nginx
|
||||||
|
io.k8s.cli.setters.my-tag-setter:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-tag-setter
|
||||||
|
value: 1.7.9
|
||||||
|
io.k8s.cli.substitutions.my-image-subst:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: my-image-subst
|
||||||
|
pattern: ${my-image-setter}::${my-tag-setter}
|
||||||
|
values:
|
||||||
|
- marker: ${my-image-setter}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
|
||||||
|
- marker: ${my-tag-setter}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
|
||||||
|
io.k8s.cli.substitutions.my-nested-subst:
|
||||||
|
x-k8s-cli:
|
||||||
|
substitution:
|
||||||
|
name: my-nested-subst
|
||||||
|
pattern: something/${my-image-subst}/${my-other-setter}
|
||||||
|
values:
|
||||||
|
- marker: ${my-image-subst}
|
||||||
|
ref: '#/definitions/io.k8s.cli.substitutions.my-image-subst'
|
||||||
|
- marker: ${my-other-setter}
|
||||||
|
ref: '#/definitions/io.k8s.cli.setters.my-other-setter'
|
||||||
|
io.k8s.cli.setters.my-other-setter:
|
||||||
|
x-k8s-cli:
|
||||||
|
setter:
|
||||||
|
name: my-other-setter
|
||||||
|
value: nginxotherthing
|
||||||
|
`,
|
||||||
|
expectedResources: `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-deployment
|
||||||
|
spec:
|
||||||
|
replicas: 3
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: something/nginx::1.7.9/nginxotherthing # {"$openapi":"my-nested-subst"}
|
||||||
|
- name: sidecar
|
||||||
|
image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
|
||||||
|
`,
|
||||||
|
err: "substitution is used in substitution my-nested-subst, please delete the parent substitution first",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i := range tests {
|
||||||
|
test := tests[i]
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
// reset the openAPI afterward
|
||||||
|
openapi.ResetOpenAPI()
|
||||||
|
defer openapi.ResetOpenAPI()
|
||||||
|
|
||||||
|
f, err := ioutil.TempFile("", "k8s-cli-")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
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")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.Remove(r.Name())
|
||||||
|
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
runner := commands.NewDeleteSubstitutionRunner("")
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
runner.Command.SetOut(out)
|
||||||
|
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
|
||||||
|
err = runner.Command.Execute()
|
||||||
|
if test.err != "" {
|
||||||
|
if !assert.NotNil(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
} else {
|
||||||
|
assert.Equal(t, err.Error(), test.err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.Equal(t, test.out, out.String()) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
actualResources, err := ioutil.ReadFile(r.Name())
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t,
|
||||||
|
strings.TrimSpace(test.expectedResources),
|
||||||
|
strings.TrimSpace(string(actualResources))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
actualOpenAPI, err := ioutil.ReadFile(f.Name())
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t,
|
||||||
|
strings.TrimSpace(test.expectedOpenAPI),
|
||||||
|
strings.TrimSpace(string(actualOpenAPI))) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,9 @@ import (
|
|||||||
"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/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/pathutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters"
|
"sigs.k8s.io/kustomize/kyaml/setters"
|
||||||
"sigs.k8s.io/kustomize/kyaml/setters2"
|
"sigs.k8s.io/kustomize/kyaml/setters2"
|
||||||
)
|
)
|
||||||
@@ -63,11 +65,35 @@ 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 {
|
openAPIFileName, err := ext.OpenAPIFileName()
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if r.IncludeSubst {
|
|
||||||
return r.ListSubstitutions(c, args)
|
openAPIPaths, err := pathutil.SubDirsWithFile(args[0], openAPIFileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(openAPIPaths) == 0 {
|
||||||
|
return errors.Errorf("unable to find %s in %s", openAPIFileName, args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// list setters for all the subpackages with openAPI file paths
|
||||||
|
for _, openAPIPath := range openAPIPaths {
|
||||||
|
r.List = setters2.List{
|
||||||
|
Name: r.List.Name,
|
||||||
|
OpenAPIFileName: openAPIFileName,
|
||||||
|
}
|
||||||
|
resourcePath := strings.TrimSuffix(openAPIPath, openAPIFileName)
|
||||||
|
fmt.Fprintf(c.OutOrStdout(), "%s\n", resourcePath)
|
||||||
|
if err := r.ListSetters(c, openAPIPath, resourcePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if r.IncludeSubst {
|
||||||
|
if err := r.ListSubstitutions(c, openAPIPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -75,13 +101,9 @@ func (r *ListSettersRunner) 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 *ListSettersRunner) ListSetters(c *cobra.Command, args []string) error {
|
func (r *ListSettersRunner) ListSetters(c *cobra.Command, openAPIPath, resourcePath string) error {
|
||||||
// use setters v2
|
// use setters v2
|
||||||
path, err := ext.GetOpenAPIFile(args)
|
if err := r.List.ListSetters(openAPIPath, resourcePath); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := r.List.ListSetters(path, args[0]); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
table := newTable(c.OutOrStdout(), r.Markdown)
|
table := newTable(c.OutOrStdout(), r.Markdown)
|
||||||
@@ -115,13 +137,9 @@ 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(c *cobra.Command, 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
|
|
||||||
}
|
|
||||||
if err := r.List.ListSubst(path); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
table := newTable(c.OutOrStdout(), r.Markdown)
|
table := newTable(c.OutOrStdout(), r.Markdown)
|
||||||
|
|||||||
@@ -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,57 @@ openAPI:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListSettersSubPackages(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
dataset string
|
||||||
|
args []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "list-replicas",
|
||||||
|
dataset: "dataset1",
|
||||||
|
args: []string{"--include-subst"},
|
||||||
|
expected: `test/testdata/dataset1/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/dataset1/mysql/nosetters/
|
||||||
|
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
||||||
|
test/testdata/dataset1/mysql/storage/
|
||||||
|
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
|
||||||
|
namespace myspace 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, test.expected, 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 # {"$openapi":"namespace"}
|
||||||
|
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 # {"$openapi":"image-tag"}
|
||||||
|
- 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 # {"$openapi":"namespace"}
|
||||||
|
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:
|
||||||
|
|||||||
@@ -177,13 +177,13 @@ func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if r.WrapKind != "" {
|
if r.WrapKind != "" {
|
||||||
if kind := rw.FunctionConfig.Field("kind"); !yaml.IsFieldEmpty(kind) {
|
if kind := rw.FunctionConfig.Field("kind"); !kind.IsNilOrEmpty() {
|
||||||
kind.Value.YNode().Value = r.WrapKind
|
kind.Value.YNode().Value = r.WrapKind
|
||||||
}
|
}
|
||||||
rw.WrappingKind = r.WrapKind
|
rw.WrappingKind = r.WrapKind
|
||||||
}
|
}
|
||||||
if r.WrapVersion != "" {
|
if r.WrapVersion != "" {
|
||||||
if version := rw.FunctionConfig.Field("apiVersion"); !yaml.IsFieldEmpty(version) {
|
if version := rw.FunctionConfig.Field("apiVersion"); !version.IsNilOrEmpty() {
|
||||||
version.Value.YNode().Value = r.WrapVersion
|
version.Value.YNode().Value = r.WrapVersion
|
||||||
}
|
}
|
||||||
rw.WrappingAPIVersion = r.WrapVersion
|
rw.WrappingAPIVersion = r.WrapVersion
|
||||||
|
|||||||
@@ -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
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
|||||||
33
cmd/config/internal/commands/test/testdata/dataset1/mysql/Krmfile
vendored
Normal file
33
cmd/config/internal/commands/test/testdata/dataset1/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/dataset1/mysql/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset1/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/dataset1/mysql/nosetters/Krmfile
vendored
Normal file
6
cmd/config/internal/commands/test/testdata/dataset1/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/dataset1/mysql/nosetters/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset1/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/dataset1/mysql/storage/Krmfile
vendored
Normal file
13
cmd/config/internal/commands/test/testdata/dataset1/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/dataset1/mysql/storage/deployment.yaml
vendored
Normal file
15
cmd/config/internal/commands/test/testdata/dataset1/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
|
||||||
@@ -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.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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="nameSuffix">
|
<meta itemprop="name" content="nameSuffix">
|
||||||
<meta itemprop="description" content="Appends the value to the names of all resources and references.
|
<meta itemprop="description" content="Appends 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="nameSuffix"/>
|
<meta name="twitter:title" content="nameSuffix"/>
|
||||||
<meta name="twitter:description" content="Appends the value to the names of all resources and references.
|
<meta name="twitter:description" content="Appends 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="patches">
|
<meta itemprop="name" content="patches">
|
||||||
<meta itemprop="description" content="Patch resources
|
<meta itemprop="description" content="Patch resources
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="patches"/>
|
<meta name="twitter:title" content="patches"/>
|
||||||
<meta name="twitter:description" content="Patch resources
|
<meta name="twitter:description" content="Patch resources
|
||||||
"/>
|
"/>
|
||||||
@@ -701,6 +702,25 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<nav id="TableOfContents">
|
||||||
|
<ul>
|
||||||
|
<li><a href="#example-i">Example I</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#intent">Intent</a></li>
|
||||||
|
<li><a href="#file-input">File Input</a></li>
|
||||||
|
<li><a href="#build-output">Build Output</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a href="#example-ii">Example II</a>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#intent-1">Intent</a></li>
|
||||||
|
<li><a href="#file-input-1">File Input</a></li>
|
||||||
|
<li><a href="#build-output-1">Build Output</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -780,7 +800,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">"op": "replace", "path": "/spec/template/containers/0/image", "value": </span><span style="color:#4e9a06">"registry/conatiner:1.0.0"</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>
|
||||||
<div class="section-index">
|
<div class="section-index">
|
||||||
|
|
||||||
|
|
||||||
@@ -835,7 +928,7 @@ is equivalent to <code>^myapp$</code>.</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 26, 2020: <a href="https://github.com/kubernetes-sigs/kustomize/commit/fbebd990a4086c967f741ae2d7fd29a24b59845e">Docs update - Patch Examples (fbebd990)</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="patchesJson6902">
|
<meta itemprop="name" content="patchesJson6902">
|
||||||
<meta itemprop="description" content="Patch resources using the [json 6902 standard](https://tools.ietf.org/html/rfc6902)
|
<meta itemprop="description" content="Patch resources using the [json 6902 standard](https://tools.ietf.org/html/rfc6902)
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="patchesJson6902"/>
|
<meta name="twitter:title" content="patchesJson6902"/>
|
||||||
<meta name="twitter:description" content="Patch resources using the [json 6902 standard](https://tools.ietf.org/html/rfc6902)
|
<meta name="twitter:description" content="Patch resources using the [json 6902 standard](https://tools.ietf.org/html/rfc6902)
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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="patchesStrategicMerge">
|
<meta itemprop="name" content="patchesStrategicMerge">
|
||||||
<meta itemprop="description" content="Patch resources using the strategic merge patch standard.
|
<meta itemprop="description" content="Patch resources using the strategic merge patch standard.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="patchesStrategicMerge"/>
|
<meta name="twitter:title" content="patchesStrategicMerge"/>
|
||||||
<meta name="twitter:description" content="Patch resources using the strategic merge patch standard.
|
<meta name="twitter:description" content="Patch resources using the strategic merge patch standard.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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="replicas">
|
<meta itemprop="name" content="replicas">
|
||||||
<meta itemprop="description" content="Change the number of replicas for a resource.
|
<meta itemprop="description" content="Change the number of replicas for a resource.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="replicas"/>
|
<meta name="twitter:title" content="replicas"/>
|
||||||
<meta name="twitter:description" content="Change the number of replicas for a resource.
|
<meta name="twitter:description" content="Change the number of replicas for a resource.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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="resources">
|
<meta itemprop="name" content="resources">
|
||||||
<meta itemprop="description" content="Resources to include.
|
<meta itemprop="description" content="Resources to include.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="resources"/>
|
<meta name="twitter:title" content="resources"/>
|
||||||
<meta name="twitter:description" content="Resources to include.
|
<meta name="twitter:description" content="Resources to include.
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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="secretGenerator">
|
<meta itemprop="name" content="secretGenerator">
|
||||||
<meta itemprop="description" content="Generate Secret resources.
|
<meta itemprop="description" content="Generate Secret resources.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="secretGenerator"/>
|
<meta name="twitter:title" content="secretGenerator"/>
|
||||||
<meta name="twitter:description" content="Generate Secret resources.
|
<meta name="twitter:description" content="Generate Secret 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="vars">
|
<meta itemprop="name" content="vars">
|
||||||
<meta itemprop="description" content="Substitute name references.
|
<meta itemprop="description" content="Substitute name references.
|
||||||
"><meta name="twitter:card" content="summary"/>
|
">
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="vars"/>
|
<meta name="twitter:title" content="vars"/>
|
||||||
<meta name="twitter:description" content="Substitute name references.
|
<meta name="twitter:description" content="Substitute name references.
|
||||||
"/>
|
"/>
|
||||||
@@ -798,7 +799,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’s
|
particular objects as specified by kustomize’s
|
||||||
configuration data.</p>
|
configuration data.</p>
|
||||||
<p>The default config data for vars is at <a href="/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
|
||||||
@@ -862,7 +863,7 @@ in the Deployment.</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 7, 2020: <a href="https://github.com/kubernetes-sigs/kustomize/commit/ca807019f093ebff3c29480f308c54121af58f1a">Add full github link to varreference.go (ca807019)</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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v1.0.1">
|
<meta itemprop="name" content="v1.0.1">
|
||||||
<meta itemprop="description" content="Kustomize v1.0.1
|
<meta itemprop="description" content="Kustomize v1.0.1
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2018-05-21T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2018-05-21T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
||||||
<meta itemprop="wordCount" content="45">
|
<meta itemprop="wordCount" content="45">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v1.0.1"/>
|
<meta name="twitter:title" content="v1.0.1"/>
|
||||||
<meta name="twitter:description" content="Kustomize v1.0.1
|
<meta name="twitter:description" content="Kustomize v1.0.1
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v2.0.0">
|
<meta itemprop="name" content="v2.0.0">
|
||||||
<meta itemprop="description" content="Kustomize v2.0.0
|
<meta itemprop="description" content="Kustomize v2.0.0
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2019-02-05T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2019-02-05T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-06-07T21:07:46-07:00" />
|
<meta itemprop="dateModified" content="2020-06-07T21:07:46-07:00" />
|
||||||
<meta itemprop="wordCount" content="458">
|
<meta itemprop="wordCount" content="458">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v2.0.0"/>
|
<meta name="twitter:title" content="v2.0.0"/>
|
||||||
<meta name="twitter:description" content="Kustomize v2.0.0
|
<meta name="twitter:description" content="Kustomize v2.0.0
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v2.1.0">
|
<meta itemprop="name" content="v2.1.0">
|
||||||
<meta itemprop="description" content="Kustomize v2.1.0
|
<meta itemprop="description" content="Kustomize v2.1.0
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2019-06-18T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2019-06-18T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
||||||
<meta itemprop="wordCount" content="920">
|
<meta itemprop="wordCount" content="920">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v2.1.0"/>
|
<meta name="twitter:title" content="v2.1.0"/>
|
||||||
<meta name="twitter:description" content="Kustomize v2.1.0
|
<meta name="twitter:description" content="Kustomize v2.1.0
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v3.0.0">
|
<meta itemprop="name" content="v3.0.0">
|
||||||
<meta itemprop="description" content="Kustomize v3.0.0
|
<meta itemprop="description" content="Kustomize v3.0.0
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2019-07-03T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2019-07-03T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
||||||
<meta itemprop="wordCount" content="324">
|
<meta itemprop="wordCount" content="324">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v3.0.0"/>
|
<meta name="twitter:title" content="v3.0.0"/>
|
||||||
<meta name="twitter:description" content="Kustomize v3.0.0
|
<meta name="twitter:description" content="Kustomize v3.0.0
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v3.1.0">
|
<meta itemprop="name" content="v3.1.0">
|
||||||
<meta itemprop="description" content="Kustomize v3.1.0
|
<meta itemprop="description" content="Kustomize v3.1.0
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2019-07-26T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2019-07-26T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
||||||
<meta itemprop="wordCount" content="362">
|
<meta itemprop="wordCount" content="362">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v3.1.0"/>
|
<meta name="twitter:title" content="v3.1.0"/>
|
||||||
<meta name="twitter:description" content="Kustomize v3.1.0
|
<meta name="twitter:description" content="Kustomize v3.1.0
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v3.2.0">
|
<meta itemprop="name" content="v3.2.0">
|
||||||
<meta itemprop="description" content="Kustomize v3.2.0
|
<meta itemprop="description" content="Kustomize v3.2.0
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2019-09-17T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2019-09-17T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
||||||
<meta itemprop="wordCount" content="103">
|
<meta itemprop="wordCount" content="103">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v3.2.0"/>
|
<meta name="twitter:title" content="v3.2.0"/>
|
||||||
<meta name="twitter:description" content="Kustomize v3.2.0
|
<meta name="twitter:description" content="Kustomize v3.2.0
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v3.2.1">
|
<meta itemprop="name" content="v3.2.1">
|
||||||
<meta itemprop="description" content="Kustomize v3.2.1
|
<meta itemprop="description" content="Kustomize v3.2.1
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2019-09-26T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2019-09-26T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-06-07T21:07:46-07:00" />
|
<meta itemprop="dateModified" content="2020-06-07T21:07:46-07:00" />
|
||||||
<meta itemprop="wordCount" content="63">
|
<meta itemprop="wordCount" content="63">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v3.2.1"/>
|
<meta name="twitter:title" content="v3.2.1"/>
|
||||||
<meta name="twitter:description" content="Kustomize v3.2.1
|
<meta name="twitter:description" content="Kustomize v3.2.1
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|
||||||
@@ -30,13 +30,14 @@
|
|||||||
<meta itemprop="name" content="v3.3.0">
|
<meta itemprop="name" content="v3.3.0">
|
||||||
<meta itemprop="description" content="Kustomize v3.3.0
|
<meta itemprop="description" content="Kustomize v3.3.0
|
||||||
">
|
">
|
||||||
<meta itemprop="datePublished" content="2019-10-24T00:00:00+00:00" />
|
<meta itemprop="datePublished" content="2019-10-24T00:00:00+00:00" />
|
||||||
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
<meta itemprop="dateModified" content="2020-07-16T12:57:18-07:00" />
|
||||||
<meta itemprop="wordCount" content="863">
|
<meta itemprop="wordCount" content="863">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<meta itemprop="keywords" content="" /><meta name="twitter:card" content="summary"/>
|
<meta itemprop="keywords" content="" />
|
||||||
|
<meta name="twitter:card" content="summary"/>
|
||||||
<meta name="twitter:title" content="v3.3.0"/>
|
<meta name="twitter:title" content="v3.3.0"/>
|
||||||
<meta name="twitter:description" content="Kustomize v3.3.0
|
<meta name="twitter:description" content="Kustomize v3.3.0
|
||||||
"/>
|
"/>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user