Compare commits

...

115 Commits

Author SHA1 Message Date
Jeff Regan
f59f12947b Update release instructions. 2020-08-28 11:39:17 -07:00
Jeff Regan
90bc96d9d8 Merge pull request #2892 from SyamSundarKirubakaran/Docs-Patch
Docs Update - patches
2020-08-28 11:32:33 -07:00
Jeff Regan
2be59aefec Merge pull request #2909 from Shell32-Natsu/remove-flags
remove --fn-user and --fn-env flags
2020-08-28 11:22:17 -07:00
Donny Xia
91b779269f remove --fn-user and --fn-env flags 2020-08-28 10:42:30 -07:00
Jeff Regan
e6ea4ad260 Merge pull request #2902 from monopole/fixcomments
Fix some generated comments.
2020-08-27 14:49:58 -07:00
jregan
f5cab0f6e1 Fix some generated comments. 2020-08-27 14:49:23 -07:00
Jeff Regan
e39afc9f68 Merge pull request #2900 from Shell32-Natsu/add-fn-env
add --fn-env flag to fn run
2020-08-27 14:38:51 -07:00
Donny Xia
3801a29d9b add --fn-env flag to fn run 2020-08-27 13:46:36 -07:00
Kubernetes Prow Robot
39cf4af638 Merge pull request #2891 from phanimarupaka/ListSettersWithSubPackages
List setters in subpackages
2020-08-27 13:43:24 -07:00
Phani Teja Marupaka
9d65dd0786 List setters in subpackages 2020-08-27 12:32:22 -07:00
Jeff Regan
3dced70850 Merge pull request #2888 from Shell32-Natsu/envs
explicitly specify envs to be exported in function
2020-08-26 15:47:43 -07:00
Jeff Regan
4356043582 Merge pull request #2847 from Shell32-Natsu/prefix-suffix-name-reference
fix name reference with prefixsuffix
2020-08-26 13:13:59 -07:00
Donny Xia
c202be0338 Remove redundant env field 2020-08-26 12:49:42 -07:00
Jeff Regan
9359155418 Update nameref.go 2020-08-26 12:00:37 -07:00
Jeff Regan
ba22bbe19e Merge pull request #2890 from Shell32-Natsu/fn-user
add --fn-user flag to function run
2020-08-26 10:44:49 -07:00
Syam Sundar K
3e5989ae18 docs/ build 2020-08-26 18:11:27 +05:30
Syam Sundar K
fbebd990a4 Docs update - Patch Examples 2020-08-26 17:46:29 +05:30
Donny Xia
646e0b4f61 add --fn-user flag to function run 2020-08-25 15:29:59 -07:00
Donny Xia
0f67692265 Add more tests 2020-08-25 14:51:01 -07:00
Donny Xia
6a7afd8694 fix name reference with prefixsuffix 2020-08-25 12:25:37 -07:00
Donny Xia
46194b3385 code review 2020-08-25 12:15:37 -07:00
Donny Xia
904a9dea08 explicitly specify envs to be exported in function 2020-08-25 12:14:46 -07:00
Jeff Regan
30b58e90a3 Merge pull request #2887 from Shell32-Natsu/network-name
refactor network name in kyaml container
2020-08-25 11:47:13 -07:00
Jeff Regan
5bdd8657a5 Merge pull request #2889 from etefera/release-note-generator
Add logging for module release notes.
2020-08-24 16:58:42 -07:00
Donny Xia
893c99da1c code review 2020-08-24 16:27:55 -07:00
Eyob Tefera
43980f8586 Remove changelog directive in yaml. 2020-08-24 21:39:51 +00:00
Eyob Tefera
0c8e033c96 Add logging for module release notes. 2020-08-24 21:21:30 +00:00
Donny Xia
fa15242719 refactor network name in kyaml container 2020-08-24 11:40:57 -07:00
Kubernetes Prow Robot
a2e080bf6c Merge pull request #2886 from monopole/deprecateOrDrown
Add types and proposal for k8s to kyaml migration.
2020-08-24 10:42:17 -07:00
jregan
e91cdb5eba Checkpoint 2020-08-23 19:46:08 -07:00
Jeff Regan
ef54f9be5a Merge pull request #2885 from monopole/cleanupOpts
Clean up option construction.
2020-08-23 12:20:52 -07:00
jregan
f051acb83c Clean up option construction. 2020-08-23 12:06:48 -07:00
Jeff Regan
bbb046081b Merge pull request #2884 from monopole/splitPreservingLineHistory
Split out rewrangler preserving line history
2020-08-23 09:18:23 -07:00
jregan
77b28a986f Split reswrangler.go and test from resmap.go 2020-08-23 08:23:13 -07:00
jregan
97bc34eb37 Delete non-api/resmap/reswrangler stuff from api/resmap/reswrangler. 2020-08-23 08:18:23 -07:00
jregan
719380f523 Copy api/resmap/resmap to api/resmap/reswrangler. 2020-08-23 08:18:23 -07:00
Jeff Regan
640ae9521b Merge pull request #2882 from monopole/unpin
Unpin transformers from kyaml.
2020-08-22 18:57:46 -07:00
jregan
1dffc7577b Unpin transformers from kyaml. 2020-08-22 18:38:48 -07:00
Jeff Regan
a0b7288329 Merge pull request #2883 from monopole/tweakCloudBuild
Tweak cloudbuild.sh release note handling
2020-08-22 18:11:26 -07:00
Jeffrey Regan
cc5617c048 Tweak cloudbuild.sh release note handling 2020-08-22 18:07:36 -07:00
Jeff Regan
a77d7e5164 Merge pull request #2875 from etefera/bundled-release-notes
Generate release notes per module.
2020-08-22 17:45:41 -07:00
Jeff Regan
40dc90b3b1 Update cloudbuild.sh 2020-08-22 17:45:16 -07:00
Jeff Regan
16229095b3 Merge pull request #2880 from monopole/merginator
Simplify use of the Merginator.
2020-08-22 08:36:22 -07:00
jregan
1d91401772 Simplify use of the Merginator. 2020-08-22 08:07:57 -07:00
Jeff Regan
007a5327d7 Merge pull request #2877 from monopole/rnodeCopy
RNode copier
2020-08-21 18:33:17 -07:00
jregan
24beeb429d RNode copier 2020-08-21 18:04:58 -07:00
Jeff Regan
9b4d4c9d46 Merge pull request #2876 from monopole/parseGv
Gvk.ParseGroupVersion function.
2020-08-21 16:16:02 -07:00
jregan
d5f868c5c7 Gvk.ParseGroupVersion function. 2020-08-21 15:40:37 -07:00
Jeff Regan
ff3f39d84b Merge pull request #2871 from Shell32-Natsu/container-user
Update kyaml to specify user for function
2020-08-21 14:19:55 -07:00
Donny Xia
451c5c32c9 code review 2020-08-21 12:05:11 -07:00
Eyob Tefera
faef5714bf Feed generated changelog as input to goreleaser. 2020-08-21 18:11:47 +00:00
Jeff Regan
7833c6edcf Merge pull request #2873 from mortent/BumpCliUtilsVersion
Bump cli-utils version to 0.19.0
2020-08-21 08:14:26 -07:00
Morten Torkildsen
a1cd23c91d Bump cli-utils version to v0.19.0 2020-08-20 20:35:46 -07:00
Jeff Regan
e39a5adc00 Merge pull request #2864 from Shell32-Natsu/fn-source-empty-dir
add option to continue pipeline when the input is empty
2020-08-20 18:09:11 -07:00
Donny Xia
b6900ead22 update TODO with issue number 2020-08-20 17:01:03 -07:00
Donny Xia
d03cf061e8 Update kyaml to specify user for function 2020-08-20 15:24:59 -07:00
Jeff Regan
8293f3002d Merge pull request #2870 from monopole/deleteNewlyUnused
Delete newly orphaned code.
2020-08-20 14:55:01 -07:00
jregan
edced4b3f6 Delete newly orphaned code. 2020-08-20 13:12:06 -07:00
Donny Xia
501684a9c6 remove break after input in pipeline 2020-08-20 11:54:56 -07:00
Jeff Regan
4e42e1a058 Merge pull request #2866 from yankeexe/patch-1
Update dead URL to base in glossary.
2020-08-20 09:33:02 -07:00
Yankee
b450b624e8 Update dead URL to base in glossary.
The term `central concept` referring to `base` was a dead link. Updated the link to go to proper definition of base.
2020-08-20 08:18:47 +05:45
Jeff Regan
0be4a61f64 Merge pull request #2794 from etefera/replace-patches-name
Replace patchesJson6902 field with patches.
2020-08-19 14:02:59 -07:00
Kubernetes Prow Robot
596c39b7bc Merge pull request #2855 from phanimarupaka/DeleteSubst
Delete substitution and fix delete setters
2020-08-19 12:27:23 -07:00
Donny Xia
037ac3b134 add option to continue pipeline when the input is empty 2020-08-19 11:21:35 -07:00
Jeff Regan
0ff4e53046 Update comment in types.go 2020-08-19 10:36:28 -07:00
Kubernetes Prow Robot
660f7f9435 Merge pull request #2859 from monopole/namespaceable
Add TypeMeta.IsNamespaceable
2020-08-19 09:31:14 -07:00
jregan
e6ee03e3e3 Add TypeMeta.IsNamespaceable 2020-08-19 06:50:12 -07:00
Phani Teja Marupaka
ca04c874f2 Delete substitution and fix delete setters 2020-08-18 22:24:50 -07:00
Kubernetes Prow Robot
cbfef858a0 Merge pull request #2854 from monopole/inlineTypeMeta
Inline repeated types.
2020-08-18 10:39:11 -07:00
Kubernetes Prow Robot
62fbfdfa21 Merge pull request #2826 from ZhuGongpu/master
Add an option to log which function is running
2020-08-17 13:25:31 -07:00
Kubernetes Prow Robot
5d72fbc6c9 Merge pull request #2848 from phanimarupaka/UpdateArraySetterComments
Update setter comments correctly on updates
2020-08-17 10:29:31 -07:00
Kubernetes Prow Robot
bc37ec9d88 Merge pull request #2853 from phanimarupaka/WarnIfNoSubstMatch
Print message if subst doesn't match any field value
2020-08-17 10:00:19 -07:00
jregan
8619c9aa13 Inline TypeMeta. 2020-08-16 17:20:30 -07:00
Phani Teja Marupaka
5d8722a786 Print message if subst doesn't match any field value 2020-08-16 15:54:34 -07:00
Jeff Regan
0d5552fca6 Merge pull request #2852 from monopole/splitPreservingLineHistory
Make mapnode.go, preserving line history.
2020-08-16 14:33:33 -07:00
jregan
8a8e35f3bb Split mapnode.go and test from types.go 2020-08-16 13:25:19 -07:00
jregan
25dbe1eaa8 Delete non-kyaml/yaml/mapnode stuff from kyaml/yaml/mapnode. 2020-08-16 13:25:19 -07:00
jregan
2289e7d2e9 Copy kyaml/yaml/types to kyaml/yaml/mapnode. 2020-08-16 13:25:19 -07:00
Jeff Regan
eb0f484e3d Merge pull request #2851 from monopole/blamePreservingSplit
Split rnode.go from types.go, preserving line history
2020-08-16 08:03:06 -07:00
jregan
d438271263 Split rnode.go and test from types.go 2020-08-16 07:36:15 -07:00
jregan
c4c8decb74 Delete non-rnode stuff from rnode.go. 2020-08-16 07:35:11 -07:00
jregan
0590b225c7 Copy types.go to rnode.go. 2020-08-16 07:35:11 -07:00
Jeff Regan
45131a6d62 Merge pull request #2850 from monopole/autoMod
Automated go.sum/format updates.
2020-08-15 19:31:40 -07:00
jregan
4dfe3c6296 Automated go.sum updates. 2020-08-15 19:18:08 -07:00
Jeff Regan
881f358228 Merge pull request #2835 from james-callahan/transformer-APIService-support
Transform namespace inside of APIService
2020-08-15 18:03:25 -07:00
Jeff Regan
86c93b9fb7 Merge pull request #2829 from msk-/master
Bug: name prefix or suffix in base results in incorrect references
2020-08-15 18:02:59 -07:00
Phani Teja Marupaka
25e30de2d6 Update setter comments correctly on updates 2020-08-15 03:46:33 -07:00
Jeff Regan
a8160356bd Merge pull request #2839 from mortent/SetterNestedPath
Add test for adding setter with path inside a sequence
2020-08-14 09:48:25 -07:00
Jeff Regan
32de6de313 Merge pull request #2840 from mortent/ImproveFieldPathDescription
Improve the description of the --field flag for create-setter
2020-08-14 09:47:42 -07:00
Jeff Regan
501ec38777 Merge pull request #2831 from Shell32-Natsu/line-break-test
Add test for long quoted string with blanks
2020-08-13 17:25:18 -07:00
Morten Torkildsen
1b2a966c62 Improve the description of the --field flag for create-setter 2020-08-12 20:21:12 -07:00
Morten Torkildsen
bcdbb1a282 Add test for adding setter with path inside a sequence 2020-08-12 20:04:39 -07:00
Jeff Regan
01f28e6779 Merge pull request #2838 from phanimarupaka/UnpinCmdConfigv0.6.0
Unpin kyaml v0.6.0 from cmd/comnfig
2020-08-12 15:43:33 -07:00
Phani Teja Marupaka
3f8e3686e2 Unpin kyaml v0.6.0 from cmd/comnfig 2020-08-12 14:45:27 -07:00
Donny Xia
6f782ac8c3 fix linter issue 2020-08-12 12:25:49 -07:00
Donny Xia
e5bc644653 change the test for blanks in string 2020-08-12 12:17:56 -07:00
Eyob Tefera
1bc9225302 Move patches fix to before Write step. 2020-08-12 18:14:03 +00:00
msk-
99d7ad6dc9 Made test pass- whoops 2020-08-12 11:53:47 +01:00
msk-
345dbc83e3 Implemented PR feedback 2020-08-12 11:49:24 +01:00
James Callahan
8ddf2297e8 Transform namespace inside of APIService 2020-08-12 17:19:25 +10:00
Gongpu Zhu
b407675fc0 Enable logging in runfn 2020-08-11 18:02:22 -07:00
Donny Xia
1366e0344a Add test for long line break 2020-08-11 13:43:18 -07:00
msk-
b71b36a213 Added test for name suffix in base not generating expected reference name 2020-08-11 10:02:25 +01:00
Gongpu Zhu
e5a78710aa Add callback to kio pipeline execution 2020-08-10 17:14:56 -07:00
Eyob Tefera
4da40461d3 Merge branch 'master' into replace-patches-name 2020-08-10 18:31:33 +00:00
Eyob Tefera
c96a4f3d73 Merge branch 'master' of https://github.com/kubernetes-sigs/kustomize 2020-08-10 18:26:16 +00:00
Eyob Tefera
45893b2588 Iteratively convert PatchesJson6902 to Patches. 2020-08-10 17:57:39 +00:00
Eyob Tefera
2f3c89e73f Add diffing to tests. 2020-08-10 17:57:03 +00:00
Eyob Tefera
eee581462c Undo wholesale replacement of PatchesJson6902. 2020-08-10 17:53:30 +00:00
Eyob Tefera
bd7d0f864b Revert "Remove unused utility function StringInSplice."
This reverts commit 43b0f2d925.
2020-08-09 07:30:40 +00:00
Eyob Tefera
b3f147d012 Add group and verison to fix test. 2020-08-05 22:03:12 +00:00
Eyob Tefera
43b0f2d925 Remove unused utility function StringInSplice. 2020-08-01 18:45:44 +00:00
Eyob Tefera
efd5f414a8 Remove unused field from field order list. 2020-08-01 18:44:30 +00:00
Eyob Tefera
9b8232533f Add test. 2020-08-01 08:55:18 +00:00
Eyob Tefera
bc5859d44b Replace patchesJson6902 field with patches. 2020-08-01 08:55:00 +00:00
283 changed files with 7269 additions and 3937 deletions

View File

@@ -64,7 +64,7 @@ func (p *PatchStrategicMergeTransformerPlugin) Config(
}
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 {
return err
}

View File

@@ -86,6 +86,18 @@ func (f Filter) setScalar(node *yaml.RNode) error {
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.
// It returns the current name and namespace of the selected candidate.
// Note that the content of the referricalCandidateSubset slice is most of the time
@@ -103,12 +115,19 @@ func selectReferral(
id := res.OrgId()
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
// If there's more than one match, there's no way
// to know which one to pick, so emit error.
// If there's more than one match,
// filter the matches by prefix and suffix
if len(matches) > 1 {
return "", "", fmt.Errorf(
"multiple matches for %s:\n %v",
id, getIds(matches))
filteredMatches := filterReferralCandidates(referrer, matches)
if len(filteredMatches) > 1 {
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
// by the referrer.

View File

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

View File

@@ -12,12 +12,13 @@ require (
github.com/yujunz/go-getter v1.4.1-lite
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff
gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0
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
)
replace sigs.k8s.io/kustomize/kyaml v0.5.0 => ../kyaml
replace sigs.k8s.io/kustomize/kyaml v0.6.1 => ../kyaml

View File

@@ -38,35 +38,68 @@ type Loader interface {
Cleanup() error
}
// Kunstructured allows manipulation of k8s objects
// that do not have Golang structs.
// Kunstructured represents a Kubernetes Resource Model object.
type Kunstructured interface {
// Several uses.
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
GetBool(path string) (bool, error)
// Used by ResAccumulator and ReplacementTransformer.
GetFieldValue(string) (interface{}, error)
GetFloat64(path string) (float64, error)
// Used by Resource.OrgId
GetGvk() resid.Gvk
GetInt64(path string) (int64, error)
// Used by resource.Factory.SliceFromBytes
GetKind() string
// Used by Resource.Replace
GetLabels() map[string]string
GetMap(path string) (map[string]interface{}, error)
// Used by Resource.CurId and resource factory.
GetName() string
// Used by special case code in
// ResMap.SubsetThatCouldBeReferencedByResource
GetSlice(path string) ([]interface{}, error)
// GetString returns the value of a string field.
// Used by Resource.GetNamespace
GetString(string) (string, error)
GetStringMap(path string) (map[string]string, error)
GetStringSlice(string) ([]string, error)
// Several uses.
Map() map[string]interface{}
// Used by Resource.AsYAML and Resource.String
MarshalJSON() ([]byte, error)
// Used by resWrangler.Select
MatchesAnnotationSelector(selector string) (bool, error)
// Used by resWrangler.Select
MatchesLabelSelector(selector string) (bool, error)
Patch(Kunstructured) error
// Used by Resource.Replace.
SetAnnotations(map[string]string)
// Used by PatchStrategicMergeTransformer.
SetGvk(resid.Gvk)
// Used by Resource.Replace and used to remove "validated by" labels.
SetLabels(map[string]string)
SetMap(map[string]interface{})
// Used by Resource.Replace.
SetName(string)
// Used by Resource.Replace.
SetNamespace(string)
// Needed, for now, by kyaml/filtersutil.ApplyToJSON.
UnmarshalJSON([]byte) error
}

View File

@@ -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/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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
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 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/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=
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/kustomize/kyaml v0.6.1 h1:mwffj5vt3MPdbWV3fZnnwol8SO7sUoGdgejBlvseyak=
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/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -1,7 +1,7 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package patch
package merge
import (
"encoding/json"
@@ -20,18 +20,20 @@ import (
type conflictDetector interface {
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)
}
type jsonMergePatch struct {
rf *resource.Factory
resourceFactory *resource.Factory
}
var _ conflictDetector = &jsonMergePatch{}
func newJMPConflictDetector(rf *resource.Factory) conflictDetector {
return &jsonMergePatch{rf: rf}
return &jsonMergePatch{resourceFactory: rf}
}
func (jmp *jsonMergePatch) hasConflict(
@@ -40,7 +42,8 @@ func (jmp *jsonMergePatch) hasConflict(
}
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 {
if i == conflictingPatchIdx {
continue
@@ -77,7 +80,7 @@ func (jmp *jsonMergePatch) mergePatches(
}
mergedMap := make(map[string]interface{})
err = json.Unmarshal(mergedBytes, &mergedMap)
return jmp.rf.FromMap(mergedMap), err
return jmp.resourceFactory.FromMap(mergedMap), err
}
type strategicMergePatch struct {
@@ -94,13 +97,15 @@ func newSMPConflictDetector(
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(
p1.Map(), p2.Map(), smp.lookupPatchMeta)
}
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 {
if i == conflictingPatchIdx {
continue
@@ -122,10 +127,12 @@ func (smp *strategicMergePatch) findConflict(
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(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
}
@@ -134,10 +141,21 @@ func (smp *strategicMergePatch) mergePatches(patch1, patch2 *resource.Resource)
return smp.rf.FromMap(mergeJSONMap), err
}
// MergePatches merge and index patches by OrgId.
// It errors out if there is conflict between patches.
func MergePatches(patches []*resource.Resource,
rf *resource.Factory) (resmap.ResMap, error) {
type merginatorImpl struct {
rf *resource.Factory
}
// 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()
for ix, patch := range patches {
id := patch.OrgId()
@@ -156,9 +174,9 @@ func MergePatches(patches []*resource.Resource,
}
var cd conflictDetector
if err != nil {
cd = newJMPConflictDetector(rf)
cd = newJMPConflictDetector(m.rf)
} else {
cd, err = newSMPConflictDetector(versionedObj, rf)
cd, err = newSMPConflictDetector(versionedObj, m.rf)
if err != nil {
return nil, err
}

View File

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

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

View File

@@ -0,0 +1,4 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package merge_test

View File

@@ -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.
// Need better solution.
if input == nil {
yaml, err := functionConfig.String()
yml, err := functionConfig.String()
if err != nil {
return nil, err
}
input = []byte(yaml)
input = []byte(yml)
}
// Configure and Execute Fn. We don't need to convert resources to ResourceList here

View File

@@ -17,7 +17,6 @@ import (
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
@@ -28,7 +27,6 @@ type KustTarget struct {
ldr ifc.Loader
validator ifc.Validator
rFactory *resmap.Factory
tFactory resmap.PatchFactory
pLdr *loader.Loader
}
@@ -37,13 +35,11 @@ func NewKustTarget(
ldr ifc.Loader,
validator ifc.Validator,
rFactory *resmap.Factory,
tFactory resmap.PatchFactory,
pLdr *loader.Loader) *KustTarget {
return &KustTarget{
ldr: ldr,
validator: validator,
rFactory: rFactory,
tFactory: tFactory,
pLdr: pLdr,
}
}
@@ -258,8 +254,7 @@ func (kt *KustTarget) runTransformers(ra *accumulator.ResAccumulator) error {
return err
}
r = append(r, lts...)
t := transform.NewMultiTransformer(r)
return ra.Transform(t)
return ra.Transform(newMultiTransformer(r))
}
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) {
resources := rm.Resources()
for _, r := range resources {
labels := r.GetLabels()
@@ -353,8 +347,7 @@ func (kt *KustTarget) accumulateComponents(
func (kt *KustTarget) accumulateDirectory(
ra *accumulator.ResAccumulator, ldr ifc.Loader, isComponent bool) (*accumulator.ResAccumulator, error) {
defer ldr.Cleanup()
subKt := NewKustTarget(
ldr, kt.validator, kt.rFactory, kt.tFactory, kt.pLdr)
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
err := subKt.Load()
if err != nil {
return nil, errors.Wrapf(

View File

@@ -7,7 +7,7 @@ import (
"testing"
"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"
"sigs.k8s.io/kustomize/api/internal/target"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
@@ -35,17 +35,17 @@ func makeKustTargetWithRf(
t *testing.T,
fSys filesys.FileSystem,
root string,
resFact *resource.Factory) *target.KustTarget {
rf := resmap.NewFactory(resFact, transformer.NewFactoryImpl())
pc := konfig.DisabledPluginConfig()
resourceFactory *resource.Factory) *target.KustTarget {
ldr, err := fLdr.NewLoader(fLdr.RestrictionRootOnly, root, fSys)
if err != nil {
t.Fatal(err)
}
rf := resmap.NewFactory(
resourceFactory, merge.NewMerginator(resourceFactory))
pc := konfig.DisabledPluginConfig()
return target.NewKustTarget(
ldr,
valtest_test.MakeFakeValidator(),
rf,
transformer.NewFactoryImpl(),
pLdr.NewLoader(pc, rf))
}

View File

@@ -1,7 +1,7 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform
package target
import (
"fmt"
@@ -17,8 +17,8 @@ type multiTransformer struct {
var _ resmap.Transformer = &multiTransformer{}
// NewMultiTransformer constructs a multiTransformer.
func NewMultiTransformer(t []resmap.Transformer) resmap.Transformer {
// newMultiTransformer constructs a multiTransformer.
func newMultiTransformer(t []resmap.Transformer) resmap.Transformer {
r := &multiTransformer{
transformers: make([]resmap.Transformer, len(t)),
checkConflictEnabled: false}

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

View File

@@ -0,0 +1,4 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package validate_test

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

View File

@@ -0,0 +1,4 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package wrappy_test

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

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

View File

@@ -17,7 +17,11 @@ import (
"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 {
hasher *kustHash
}

View File

@@ -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) {
tests := []struct {
name string

View File

@@ -14,11 +14,14 @@ import (
v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
"sigs.k8s.io/kustomize/api/ifc"
)
// KustValidator validates Labels and annotations by apimachinery
type KustValidator struct{}
var _ ifc.Validator = (*KustValidator)(nil)
// NewKustValidator returns a KustValidator object
func NewKustValidator() *KustValidator {
return &KustValidator{}

View File

@@ -12,5 +12,9 @@ namespace:
kind: RoleBinding
- path: subjects
kind: ClusterRoleBinding
- path: spec/service/namespace
group: apiregistration.k8s.io
kind: APIService
create: true
`
)

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

View File

@@ -8,16 +8,13 @@ import (
"sigs.k8s.io/kustomize/api/builtins"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer"
pLdr "sigs.k8s.io/kustomize/api/internal/plugins/loader"
"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/krusty/internal/provider"
fLdr "sigs.k8s.io/kustomize/api/loader"
"sigs.k8s.io/kustomize/api/provenance"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
)
@@ -28,13 +25,18 @@ import (
// number of overlays and bases), then make a Kustomizer
// injected with the given fileystem, then call Run.
type Kustomizer struct {
fSys filesys.FileSystem
options *Options
fSys filesys.FileSystem
options *Options
depProvider *provider.DepProvider
}
// MakeKustomizer returns an instance of 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.
@@ -49,11 +51,9 @@ func MakeKustomizer(fSys filesys.FileSystem, o *Options) *Kustomizer {
// on any number of internal paths (e.g. the filesystem may contain
// multiple overlays, and Run can be called on each of them).
func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
pf := transformer.NewFactoryImpl()
rf := resmap.NewFactory(
resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()),
pf)
resmapFactory := resmap.NewFactory(
b.depProvider.GetResourceFactory(),
b.depProvider.GetMerginator())
lr := fLdr.RestrictionNone
if b.options.LoadRestrictions == types.LoadRestrictionsRootOnly {
lr = fLdr.RestrictionRootOnly
@@ -65,10 +65,9 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
defer ldr.Cleanup()
kt := target.NewKustTarget(
ldr,
validator.NewKustValidator(),
rf,
pf,
pLdr.NewLoader(b.options.PluginConfig, rf),
b.depProvider.GetFieldValidator(),
resmapFactory,
pLdr.NewLoader(b.options.PluginConfig, resmapFactory),
)
err = kt.Load()
if err != nil {
@@ -84,7 +83,9 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
}
if b.options.AddManagedbyLabel {
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{{
Path: "metadata/labels",
CreateIfNotPresent: true,

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

View File

@@ -32,14 +32,20 @@ type Options struct {
// Options related to kustomize plugins.
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.
func MakeDefaultOptions() *Options {
return &Options{
DoLegacyResourceSort: true,
DoLegacyResourceSort: false,
AddManagedbyLabel: false,
LoadRestrictions: types.LoadRestrictionsRootOnly,
DoPrune: false,
PluginConfig: konfig.DisabledPluginConfig(),
UseKyaml: false,
}
}

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

View File

@@ -199,7 +199,9 @@ func (fl *fileLoader) New(path string) (ifc.Loader, error) {
}
root, errDir := demandDirectoryRoot(fl.fSys, fl.root.Join(path))
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 {
return nil, errDir

View File

@@ -39,5 +39,7 @@ func NewLoader(
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)
}

View File

@@ -22,8 +22,15 @@ func FromKind(k string) Gvk {
}
}
// GvkFromString makes a Gvk with a string,
// which is constructed by String() function
// ParseGroupVersion parses a KRM metadata apiVersion field.
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 {
values := strings.Split(s, fieldSep)
g := values[0]

View File

@@ -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) {
type testCase struct {
description string

View File

@@ -11,15 +11,24 @@ import (
"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.
type Factory struct {
// Makes resources.
resF *resource.Factory
tf PatchFactory
// Makes ResMaps via merging.
pm Merginator
}
// NewFactory returns a new resmap.Factory.
func NewFactory(rf *resource.Factory, tf PatchFactory) *Factory {
return &Factory{resF: rf, tf: tf}
func NewFactory(rf *resource.Factory, pm Merginator) *Factory {
return &Factory{resF: rf, pm: pm}
}
// RF returns a resource.Factory.
@@ -87,6 +96,7 @@ func (rmF *Factory) NewResMapFromConfigMapArgs(
return newResMapFromResourceSlice(resources)
}
// FromConfigMapArgs creates a new ResMap containing one ConfigMap.
func (rmF *Factory) FromConfigMapArgs(
kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) {
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
@@ -111,6 +121,7 @@ func (rmF *Factory) NewResMapFromSecretArgs(
return newResMapFromResourceSlice(resources)
}
// FromSecretArgs creates a new ResMap containing one secret.
func (rmF *Factory) FromSecretArgs(
kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) {
res, err := rmF.resF.MakeSecret(kvLdr, &args)
@@ -120,12 +131,14 @@ func (rmF *Factory) FromSecretArgs(
return rmF.FromResource(res), nil
}
func (rmF *Factory) MergePatches(patches []*resource.Resource) (
ResMap, error) {
return rmF.tf.MergePatches(patches, rmF.resF)
// Merge creates a new ResMap by merging incoming resources.
// Error if conflict found.
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()
for _, res := range resources {
err := result.Append(res)

View File

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

View File

@@ -6,16 +6,10 @@
package resmap
import (
"bytes"
"fmt"
"regexp"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
// A Transformer modifies an instance of ResMap.
@@ -242,569 +236,3 @@ type ResMap interface {
// are selected by a Selector
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
}

View File

@@ -3,899 +3,4 @@
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 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")
}
}
// See reswrangler_test.go

581
api/resmap/reswrangler.go Normal file
View 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
}

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

View File

@@ -14,55 +14,9 @@ import (
"sigs.k8s.io/yaml"
)
// Resource is a representation of a Kubernetes Resource Model object paired
// with metadata used by kustomize.
//
// 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.
//
// Resource is a representation of a Kubernetes Resource Model (KRM) object
// paired with metadata used by kustomize.
// For more history, see sigs.k8s.io/kustomize/api/ifc.Unstructured
type Resource struct {
kunStr ifc.Kunstructured
originalName string
@@ -75,7 +29,7 @@ type Resource struct {
}
func (r *Resource) ResetPrimaryData(incoming *Resource) {
r.kunStr = incoming.kunStr.Copy()
r.kunStr = incoming.Copy()
}
func (r *Resource) GetAnnotations() map[string]string {
@@ -86,26 +40,14 @@ func (r *Resource) Copy() ifc.Kunstructured {
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) {
return r.kunStr.GetFieldValue(f)
}
func (r *Resource) GetFloat64(p string) (float64, error) {
return r.kunStr.GetFloat64(p)
}
func (r *Resource) GetGvk() resid.Gvk {
return r.kunStr.GetGvk()
}
func (r *Resource) GetInt64(p string) (int64, error) {
return r.kunStr.GetInt64(p)
}
func (r *Resource) GetKind() string {
return r.kunStr.GetKind()
}
@@ -114,10 +56,6 @@ func (r *Resource) GetLabels() map[string]string {
return r.kunStr.GetLabels()
}
func (r *Resource) GetMap(p string) (map[string]interface{}, error) {
return r.kunStr.GetMap(p)
}
func (r *Resource) GetName() string {
return r.kunStr.GetName()
}
@@ -130,14 +68,6 @@ func (r *Resource) GetString(p string) (string, error) {
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{} {
return r.kunStr.Map()
}
@@ -154,10 +84,6 @@ func (r *Resource) MatchesAnnotationSelector(selector string) (bool, error) {
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) {
r.kunStr.SetAnnotations(m)
}
@@ -170,10 +96,6 @@ func (r *Resource) SetLabels(m map[string]string) {
r.kunStr.SetLabels(m)
}
func (r *Resource) SetMap(m map[string]interface{}) {
r.kunStr.SetMap(m)
}
func (r *Resource) SetName(n string) {
r.kunStr.SetName(n)
}
@@ -205,7 +127,7 @@ type ResCtxMatcher func(ResCtx) bool
// DeepCopy returns a new copy of resource
func (r *Resource) DeepCopy() *Resource {
rc := &Resource{
kunStr: r.kunStr.Copy(),
kunStr: r.Copy(),
}
rc.copyOtherFields(r)
return rc
@@ -213,11 +135,10 @@ func (r *Resource) DeepCopy() *Resource {
// Replace performs replace with other resource.
func (r *Resource) Replace(other *Resource) {
r.kunStr.SetLabels(mergeStringMaps(other.kunStr.GetLabels(), r.kunStr.GetLabels()))
r.kunStr.SetAnnotations(
mergeStringMaps(other.kunStr.GetAnnotations(), r.kunStr.GetAnnotations()))
r.kunStr.SetName(other.GetName())
r.kunStr.SetNamespace(other.GetNamespace())
r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels()))
r.SetAnnotations(mergeStringMaps(other.GetAnnotations(), r.GetAnnotations()))
r.SetName(other.GetName())
r.SetNamespace(other.GetNamespace())
r.copyOtherFields(other)
}
@@ -258,7 +179,7 @@ func (r *Resource) KunstructEqual(o *Resource) bool {
// Merge performs merge with other resource.
func (r *Resource) Merge(other *Resource) {
r.Replace(other)
mergeConfigmap(r.kunStr.Map(), other.Map(), r.Map())
mergeConfigmap(r.Map(), other.Map(), r.Map())
}
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())
}
// 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 {
return r.originalName
}
@@ -384,7 +299,7 @@ func (r *Resource) setOriginalNs(n string) *Resource {
// String returns resource as JSON.
func (r *Resource) String() string {
bs, err := r.kunStr.MarshalJSON()
bs, err := r.MarshalJSON()
if err != nil {
return "<" + err.Error() + ">"
}
@@ -394,7 +309,7 @@ func (r *Resource) String() string {
// AsYAML returns the resource in Yaml form.
// Easier to read than JSON.
func (r *Resource) AsYAML() ([]byte, error) {
json, err := r.kunStr.MarshalJSON()
json, err := r.MarshalJSON()
if err != nil {
return nil, err
}
@@ -419,7 +334,7 @@ func (r *Resource) NeedHashSuffix() bool {
// GetNamespace returns the namespace the resource thinks it's in.
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.
return namespace
}
@@ -429,7 +344,7 @@ func (r *Resource) GetNamespace() string {
// TODO: compute this once and save it in the resource.
func (r *Resource) OrgId() resid.ResId {
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
@@ -437,7 +352,7 @@ func (r *Resource) OrgId() resid.ResId {
// This should be unique in any ResMap.
func (r *Resource) CurId() resid.ResId {
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

View File

@@ -16,7 +16,7 @@ import (
"sigs.k8s.io/kustomize/api/types"
)
// Harness manages a kustomize environment for tests.
// Harness manages a test environment.
type Harness struct {
t *testing.T
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.
func (th Harness) MakeOptionsPluginsDisabled() krusty.Options {
return krusty.Options{
LoadRestrictions: types.LoadRestrictionsRootOnly,
PluginConfig: konfig.DisabledPluginConfig(),
}
return *krusty.MakeDefaultOptions()
}
// Enables use of non-builtin plugins.
func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
c, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
pc, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
if err != nil {
if strings.Contains(err.Error(), "unable to find plugin root") {
th.t.Log(
@@ -89,10 +86,9 @@ func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
}
th.t.Fatal(err)
}
return krusty.Options{
LoadRestrictions: types.LoadRestrictionsRootOnly,
PluginConfig: c,
}
o := *krusty.MakeDefaultOptions()
o.PluginConfig = pc
return o
}
// Run, failing on error.

View File

@@ -8,7 +8,7 @@ import (
"sigs.k8s.io/kustomize/api/filesys"
"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"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/konfig"
@@ -46,16 +46,17 @@ func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
if err != nil {
t.Fatal(err)
}
rf := resmap.NewFactory(
resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()),
transformer.NewFactoryImpl())
resourceFactory := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
resmapFactory := resmap.NewFactory(
resourceFactory,
merge.NewMerginator(resourceFactory))
result := &HarnessEnhanced{
Harness: MakeHarness(t),
pte: pte,
rf: rf,
pl: pLdr.NewLoader(pc, rf)}
rf: resmapFactory,
pl: pLdr.NewLoader(pc, resmapFactory)}
// Point the file loader to the root ('/') of the in-memory file system.
result.ResetLoaderRoot(filesys.Separator)

View File

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

View File

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

View File

@@ -9,14 +9,14 @@ import (
"sigs.k8s.io/yaml"
)
// FixKustomizationPreUnmarshalling modies the raw data
// FixKustomizationPreUnmarshalling modifies the raw data
// before marshalling - e.g. changes old field names to
// new field names.
func FixKustomizationPreUnmarshalling(data []byte) ([]byte, error) {
deprecateFieldsMap := map[string]string{
deprecatedFieldsMap := map[string]string{
"imageTags:": "images:",
}
for oldname, newname := range deprecateFieldsMap {
for oldname, newname := range deprecatedFieldsMap {
pattern := regexp.MustCompile(oldname)
data = pattern.ReplaceAll(data, []byte(newname))
}

View File

@@ -144,6 +144,7 @@ type Kustomization struct {
// moving content of deprecated fields to newer
// fields.
func (k *Kustomization) FixKustomizationPostUnmarshalling() {
if k.Kind == "" {
k.Kind = KustomizationKind
}
@@ -158,6 +159,17 @@ func (k *Kustomization) FixKustomizationPostUnmarshalling() {
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 {
var errs []string
if k.Kind != "" && k.Kind != KustomizationKind && k.Kind != ComponentKind {

View File

@@ -5,9 +5,11 @@ import (
)
func fixKustomizationPostUnmarshallingCheck(k, e *Kustomization) bool {
return (k.Kind == e.Kind && k.APIVersion == e.APIVersion &&
len(k.Resources) == len(e.Resources) && k.Resources[0] == e.Resources[0] &&
k.Bases == nil)
return k.Kind == e.Kind &&
k.APIVersion == e.APIVersion &&
len(k.Resources) == len(e.Resources) &&
k.Resources[0] == e.Resources[0] &&
k.Bases == nil
}
func TestFixKustomizationPostUnmarshalling(t *testing.T) {

View File

@@ -19,3 +19,9 @@ type PatchJson6902 struct {
// inline patch string
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}
}

View File

@@ -13,3 +13,8 @@ type PatchTarget struct {
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
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}
}

View File

@@ -20,6 +20,7 @@ var (
CreateSetter = commands.CreateSetterCommand
CreateSubstitution = commands.CreateSubstitutionCommand
DeleteSetter = commands.DeleteSetterCommand
DeleteSubstitution = commands.DeleteSubstitutionCommand
Fmt = commands.FmtCommand
Grep = commands.GrepCommand
Init = commands.InitCommand

View File

@@ -54,9 +54,9 @@ func GetLive(name string) *cobra.Command {
cmd.AddCommand(
applyCmd,
initcmd.NewCmdInit(ioStreams),
preview.NewCmdPreview(f, ioStreams),
preview.GetPreviewRunner(f, ioStreams).Command,
diff.NewCmdDiff(f, ioStreams),
destroy.NewCmdDestroy(f, ioStreams))
destroy.GetDestroyRunner(f, ioStreams).Command)
return cmd
}

View File

@@ -12,3 +12,13 @@ import (
var GetOpenAPIFile = func(args []string) (string, error) {
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
}

View File

@@ -14,6 +14,8 @@ require (
k8s.io/cli-runtime v0.17.3
k8s.io/client-go v0.17.3
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/cli-utils v0.18.0
sigs.k8s.io/kustomize/kyaml v0.6.0
sigs.k8s.io/cli-utils v0.19.0
sigs.k8s.io/kustomize/kyaml v0.6.1
)
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml

View File

@@ -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/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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
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 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-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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
@@ -617,19 +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/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
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.17.0/go.mod h1:9Jqm9K2W6ShhCxsEuaz6HSRKKOXigPUx3ZfypGgxBLY=
sigs.k8s.io/cli-utils v0.18.0 h1:PPFUhhwKsdMiYYm1DY9lZursNWSAEj8FYgMCZKVvOkQ=
sigs.k8s.io/cli-utils v0.18.0/go.mod h1:B7KdqkSkHNIUn3cFbaR4aKUZMKtr+Benboi1w/HW/Fg=
sigs.k8s.io/cli-utils v0.19.0 h1:lAoR5okhSV/dIusodaQp5VbDpHMcKnvjqKYHU+AB3a4=
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/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
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/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/kustomize/kyaml v0.6.0 h1:Z/9TxsiG21sbcd6JD4IeM6BVZ2+04001KKzbxCf+qeY=
sigs.k8s.io/kustomize/kyaml v0.6.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-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=

View File

@@ -39,8 +39,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
set.Flags().StringVar(&r.Set.SetPartialField.Description, "description", "",
"record a description for the current setter value.")
set.Flags().StringVar(&r.Set.SetPartialField.Field, "field", "",
"name of the field to set -- e.g. --field port. defaults to all fields match"+
"VALUE. maybe be the field name, field path, or partial field path (suffix)")
"name of the field to set, a suffix of the path to the field, or the full"+
" path to the field. Default is to match all fields.")
set.Flags().StringVar(&r.Set.ResourceMeta.Name, "name", "",
"name of the Resource on which to create the setter.")
set.Flags().MarkHidden("name")

View File

@@ -7,6 +7,7 @@ import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"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/setters2/settersutil"
)
@@ -16,7 +17,7 @@ func NewDeleteSetterRunner(parent string) *DeleteSetterRunner {
r := &DeleteSetterRunner{}
c := &cobra.Command{
Use: "delete-setter DIR NAME",
Args: cobra.MinimumNArgs(2),
Args: cobra.ExactArgs(2),
Short: commands.DeleteSetterShort,
Long: commands.DeleteSetterLong,
Example: commands.DeleteSetterExamples,
@@ -42,6 +43,7 @@ type DeleteSetterRunner struct {
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
var err error
r.DeleteSetter.Name = args[1]
r.DeleteSetter.DefinitionPrefix = fieldmeta.SetterDefinitionPrefix
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
if err != nil {

View File

@@ -30,25 +30,25 @@ func TestDeleteSetterCommand(t *testing.T) {
}{
{
name: "delete replicas",
args: []string{"replicas", "hello world"},
args: []string{"replicas-setter"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi" : "replicas"}}
replicas: 3 # {"$openapi" : "replicas-setter"}
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
io.k8s.cli.setters.replicas-setter:
description: hello world
x-k8s-cli:
setter:
name: replicas
name: replicas-setter
value: "3"
setBy: me
`,
@@ -67,32 +67,33 @@ spec:
},
{
name: "delete only one setter",
args: []string{"replicas", "hello world"},
args: []string{"replicas-setter"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi" : "replicas"}}
replicas: 3 # {"$openapi" : "replicas-setter"}
foo: nginx # {"$openapi" : "image"}
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
io.k8s.cli.setters.replicas-setter:
description: hello world
x-k8s-cli:
setter:
name: replicas
name: replicas-setter
value: "3"
setBy: me
io.k8s.cli.setters.image:
x-k8s-cli:
setter:
name: image
value: 1.0
value: nginx
`,
expectedOpenAPI: `
apiVersion: v1alpha1
@@ -103,7 +104,7 @@ openAPI:
x-k8s-cli:
setter:
name: image
value: 1.0
value: nginx
`,
expectedResources: `
apiVersion: apps/v1
@@ -112,29 +113,95 @@ metadata:
name: nginx-deployment
spec:
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",
args: []string{"image", "hello world"},
args: []string{"image"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi" : "replicas"}}
replicas: 3 # {"$openapi" : "replicas-setter"}
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
io.k8s.cli.setters.replicas-setter:
description: hello world
x-k8s-cli:
setter:
name: replicas
name: replicas-setter
value: "3"
setBy: me
`,
@@ -143,11 +210,11 @@ apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
io.k8s.cli.setters.replicas-setter:
description: hello world
x-k8s-cli:
setter:
name: replicas
name: replicas-setter
value: "3"
setBy: me
`,
@@ -157,13 +224,13 @@ kind: Deployment
metadata:
name: nginx-deployment
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",
args: []string{"image-name", "hello world"},
args: []string{"image-name"},
input: `
apiVersion: apps/v1
kind: Deployment
@@ -220,7 +287,7 @@ openAPI:
apiVersion: apps/v1
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 {

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

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

View File

@@ -13,7 +13,9 @@ import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"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/pathutil"
"sigs.k8s.io/kustomize/kyaml/setters"
"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 {
if setterVersion == "v2" {
if err := r.ListSetters(c, args); err != nil {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
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
}
@@ -75,13 +101,9 @@ func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
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
path, err := ext.GetOpenAPIFile(args)
if err != nil {
return err
}
if err := r.List.ListSetters(path, args[0]); err != nil {
if err := r.List.ListSetters(openAPIPath, resourcePath); err != nil {
return err
}
table := newTable(c.OutOrStdout(), r.Markdown)
@@ -115,13 +137,9 @@ func (r *ListSettersRunner) ListSetters(c *cobra.Command, args []string) error {
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
path, err := ext.GetOpenAPIFile(args)
if err != nil {
return err
}
if err := r.List.ListSubst(path); err != nil {
if err := r.List.ListSubst(openAPIPath); err != nil {
return err
}
table := newTable(c.OutOrStdout(), r.Markdown)

View File

@@ -7,11 +7,11 @@ import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"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"
)
@@ -408,27 +408,19 @@ openAPI:
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
f, err := ioutil.TempFile("", "k8s-cli-")
dir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
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) {
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) {
t.FailNow()
}
@@ -436,18 +428,18 @@ openAPI:
runner := commands.NewListSettersRunner("")
actual := &bytes.Buffer{}
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()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, test.expected, actual.String()) {
if !assert.Contains(t, actual.String(), test.expected) {
t.FailNow()
}
// 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) {
t.FailNow()
}
@@ -457,7 +449,7 @@ openAPI:
t.FailNow()
}
actualOpenAPI, err := ioutil.ReadFile(f.Name())
actualOpenAPI, err := ioutil.ReadFile(filepath.Join(dir, "Krmfile"))
if !assert.NoError(t, err) {
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()
}
})
}
}

View File

@@ -178,6 +178,51 @@ items:
outputOverride = `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
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
kind: Deployment
metadata:

View File

@@ -34,7 +34,8 @@ openAPI:
`,
},
expectedStdOut: `
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
./
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
replicas 3 1 No
`,
},

View 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

View 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"}

View File

@@ -0,0 +1,6 @@
apiVersion: krm.dev/v1alpha1
kind: Krmfile
metadata:
name: storage
packageMetadata:
shortDescription: sample description

View 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

View 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

View 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

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="Glossary">
<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:description" content="Glossary of terms
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="API Reference">
<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:description" content="Reference for Kustomize client-side APIs
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="bases">
<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: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>
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>
<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>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="commonAnnotations">
<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:description" content="Add annotations to add all resources.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="commonLabels">
<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:description" content="Add labels and selectors to add all resources.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="components">
<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:description" content="Compose kustomizations.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="configMapGenerator">
<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:description" content="Generate ConfigMap resources.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="crds">
<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:description" content="Adding CRD support
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<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 name="twitter:card" content="summary"/>
">
<meta name="twitter:card" content="summary"/>
<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.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="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:description" content="Modify the name, tags and/or digest for images.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="kustomization.yaml">
<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:description" content="kustomization.yaml fields and API
"/>

View File

@@ -31,7 +31,7 @@
&lt;p&gt;Move entries into the &lt;a href=&#34;https://kubernetes-sigs.github.io/kustomize/kustomize/api-reference/kustomization/resources&#34;&gt;resources&lt;/a&gt;
field. This allows bases - which are still a
&lt;a href=&#34;https://kubernetes-sigs.github.io/kustomize/kustomize/api-reference/kustomization/glossary#base&#34;&gt;central concept&lt;/a&gt; - to be
&lt;a href=&#34;https://kubernetes-sigs.github.io/kustomize/kustomize/api-reference/glossary#base&#34;&gt;central concept&lt;/a&gt; - to be
ordered relative to other input resources.&lt;/p&gt;
</description>
@@ -492,7 +492,80 @@ to it (regular expressions).&lt;/p&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;namespace&lt;/code&gt; fields of the patch target selector are
automatically anchored regular expressions. This means that the value &lt;code&gt;myapp&lt;/code&gt;
is equivalent to &lt;code&gt;^myapp$&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Consider the following &lt;code&gt;deployment.yaml&lt;/code&gt; common for both the examples:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;replicas&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0000cf;font-weight:bold&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;template&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;containers&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;- &lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-container&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;registry/conatiner&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;latest&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;example-i&#34;&gt;Example I&lt;/h2&gt;
&lt;h3 id=&#34;intent&#34;&gt;Intent&lt;/h3&gt;
&lt;p&gt;To Make the container image point to a specific version and not to the latest container in the
registry.&lt;/p&gt;
&lt;h3 id=&#34;file-input&#34;&gt;File Input&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#8f5902;font-style:italic&#34;&gt;# kustomization.yaml&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;resources&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;- deployment.yaml&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;patches&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;patch.yaml&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#8f5902;font-style:italic&#34;&gt;# patch.yaml&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;template&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;containers&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;- &lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-container&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;registry/conatiner&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#0000cf;font-weight:bold&#34;&gt;1.0.0&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;build-output&#34;&gt;Build Output&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;replicas&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0000cf;font-weight:bold&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;template&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;containers&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;- &lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;registry/conatiner&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#0000cf;font-weight:bold&#34;&gt;1.0.0&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-container&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;example-ii&#34;&gt;Example II&lt;/h2&gt;
&lt;h3 id=&#34;intent-1&#34;&gt;Intent&lt;/h3&gt;
&lt;p&gt;To Make the container image point to a specific version and not to the latest container in the
registry.&lt;/p&gt;
&lt;h3 id=&#34;file-input-1&#34;&gt;File Input&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#8f5902;font-style:italic&#34;&gt;# kustomization.yaml&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;resources&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;- deployment.yaml&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;patches&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;target&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;patch.json&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#8f5902;font-style:italic&#34;&gt;# patch.json&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;[&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;{&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;&amp;#34;op&amp;#34;: &amp;#34;replace&amp;#34;, &amp;#34;path&amp;#34;: &amp;#34;/spec/template/containers/0/image&amp;#34;, &amp;#34;value&amp;#34;: &lt;/span&gt;&lt;span style=&#34;color:#4e9a06&#34;&gt;&amp;#34;registry/conatiner:1.0.0&amp;#34;&lt;/span&gt;}&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;]&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;build-output-1&#34;&gt;Build Output&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre style=&#34;background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;apiVersion&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;kind&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;metadata&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-deployment&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;spec&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;replicas&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#0000cf;font-weight:bold&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;template&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;containers&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;- &lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;image&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;registry/conatiner&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#0000cf;font-weight:bold&#34;&gt;1.0.0&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#204a87;font-weight:bold&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#000;font-weight:bold&#34;&gt;:&lt;/span&gt;&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt; &lt;/span&gt;the-container&lt;span style=&#34;color:#f8f8f8;text-decoration:underline&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</description>
</item>
@@ -786,7 +859,7 @@ container number 2 of some pod template.&lt;/p&gt;
can only be placed in particular fields of
particular objects as specified by kustomize&amp;rsquo;s
configuration data.&lt;/p&gt;
&lt;p&gt;The default config data for vars is at &lt;a href=&#34;https://kubernetes-sigs.github.io/kustomize/konfig/builtinpluginconsts/varreference.go&#34;&gt;/api/konfig/builtinpluginconsts/varreference.go&lt;/a&gt;
&lt;p&gt;The default config data for vars is at &lt;a href=&#34;https://github.com/kubernetes-sigs/kustomize/blob/master/api/konfig/builtinpluginconsts/varreference.go&#34;&gt;/api/konfig/builtinpluginconsts/varreference.go&lt;/a&gt;
Long story short, the default targets are all
container command args and env value fields.&lt;/p&gt;
&lt;p&gt;Vars should &lt;em&gt;not&lt;/em&gt; be used for inserting names in

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="namePrefix">
<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:description" content="Prepends the value to the names of all resources and references.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="namespace">
<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:description" content="Adds namespace to all resources.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="nameSuffix">
<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:description" content="Appends the value to the names of all resources and references.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="patches">
<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: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>
@@ -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
automatically anchored regular expressions. This means that the value <code>myapp</code>
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>
<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>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="patchesJson6902">
<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:description" content="Patch resources using the [json 6902 standard](https://tools.ietf.org/html/rfc6902)
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="patchesStrategicMerge">
<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:description" content="Patch resources using the strategic merge patch standard.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="replicas">
<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:description" content="Change the number of replicas for a resource.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="resources">
<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:description" content="Resources to include.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="secretGenerator">
<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:description" content="Generate Secret resources.
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -29,7 +29,8 @@
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="vars">
<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: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
particular objects as specified by kustomize&rsquo;s
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
container command args and env value fields.</p>
<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>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v1.0.1">
<meta itemprop="description" content="Kustomize v1.0.1
">
<meta itemprop="datePublished" content="2018-05-21T00:00:00&#43;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="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:description" content="Kustomize v1.0.1
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v2.0.0">
<meta itemprop="description" content="Kustomize v2.0.0
">
<meta itemprop="datePublished" content="2019-02-05T00:00:00&#43;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="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:description" content="Kustomize v2.0.0
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v2.1.0">
<meta itemprop="description" content="Kustomize v2.1.0
">
<meta itemprop="datePublished" content="2019-06-18T00:00:00&#43;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="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:description" content="Kustomize v2.1.0
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v3.0.0">
<meta itemprop="description" content="Kustomize v3.0.0
">
<meta itemprop="datePublished" content="2019-07-03T00:00:00&#43;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="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:description" content="Kustomize v3.0.0
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v3.1.0">
<meta itemprop="description" content="Kustomize v3.1.0
">
<meta itemprop="datePublished" content="2019-07-26T00:00:00&#43;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="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:description" content="Kustomize v3.1.0
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v3.2.0">
<meta itemprop="description" content="Kustomize v3.2.0
">
<meta itemprop="datePublished" content="2019-09-17T00:00:00&#43;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="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:description" content="Kustomize v3.2.0
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v3.2.1">
<meta itemprop="description" content="Kustomize v3.2.1
">
<meta itemprop="datePublished" content="2019-09-26T00:00:00&#43;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="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:description" content="Kustomize v3.2.1
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -30,13 +30,14 @@
<meta itemprop="name" content="v3.3.0">
<meta itemprop="description" content="Kustomize v3.3.0
">
<meta itemprop="datePublished" content="2019-10-24T00:00:00&#43;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="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:description" content="Kustomize v3.3.0
"/>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<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">
@@ -27,7 +27,8 @@
<meta property="og:url" content="https://kubernetes-sigs.github.io/kustomize/blog/" />
<meta property="og:site_name" content="Kustomize" />
<meta itemprop="name" content="Kustomize Blog">
<meta itemprop="description" content="Kubernetes Configuration Customization"><meta name="twitter:card" content="summary"/>
<meta itemprop="description" content="Kubernetes Configuration Customization">
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="Kustomize Blog"/>
<meta name="twitter:description" content="Kubernetes Configuration Customization"/>

Some files were not shown because too many files have changed in this diff Show More