Compare commits

...

102 Commits

Author SHA1 Message Date
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
Kubernetes Prow Robot
b47e34ea5e Merge pull request #2836 from phanimarupaka/BumpKyamlAndCliUtils
Bump kyaml and cli-utils in cmd/config
2020-08-12 13:05:15 -07:00
Phani Teja Marupaka
762e587471 Bump kyaml and cli-utils in cmd/config 2020-08-12 12:49:31 -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
Jeff Regan
fd5eeb1645 Merge pull request #2832 from Shell32-Natsu/fix-cmdxargs
Fix IsFieldEmpty in cmdxargs
2020-08-11 16:39:46 -07:00
Donny Xia
ff5051711f fix test failure 2020-08-11 16:21:50 -07:00
Donny Xia
51268a5f06 Fix IsFieldEmpty in cmdxargs 2020-08-11 16:11:23 -07:00
Donny Xia
1366e0344a Add test for long line break 2020-08-11 13:43:18 -07:00
Jeff Regan
9be38e815e Merge pull request #2830 from kubernetes-sigs/drop-link-from-readme
Drop link to github release page from main readme
2020-08-11 12:59:26 -07:00
Jeff Regan
88c318bf46 Drop link to github release page from main readme
Fix #2823
2020-08-11 12:52:59 -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
Jeff Regan
7ee75c33a9 Merge pull request #2824 from monopole/deleteIsFieldEmpty
Remove unused IsFieldEmpty function, add more godoc.
2020-08-10 12:54:41 -07:00
jregan
2b328eeb36 Remove unused IsFieldEmpty, add more godoc. 2020-08-10 12:37:13 -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
Jeff Regan
616363ee73 Merge pull request #2822 from monopole/punchDrunk
IsFieldEmpty renamed to MapNode.IsNilOrEmpty
2020-08-10 10:46:39 -07:00
jregan
0e13eadd7a IsFieldEmpty renamed to MapNode.IsNilOrEmpty 2020-08-10 10:24:02 -07:00
Jeff Regan
0c37388135 Merge pull request #2821 from monopole/moveIsNilToMethod
Testing for nilness, nullness and emptyness.
2020-08-10 10:23:16 -07:00
Jeff Regan
5d1352882b Merge pull request #2815 from fknipp/patch-1
Add full github link to varreference.go
2020-08-09 17:30:23 -07:00
jregan
c469e80cad Add RNode.IsNilOrEmpty and test. 2020-08-09 12:37:12 -07:00
jregan
5559601ecb Add tests for IsTaggedNull, IsYNodeEmptySeq and IsYNodeEmptyMap. 2020-08-09 12:29:35 -07:00
jregan
01b34c8ea0 Add tests for IsNil 2020-08-09 11:55:28 -07:00
Jeff Regan
eba0ffdde2 Merge pull request #2820 from monopole/dropIsEmptyCalls
Replace all calls to yaml.IsEmpty with IsMissingOrNull
2020-08-09 11:09:57 -07:00
jregan
31c59bd7f2 Drop all calls to IsEmpty. 2020-08-09 10:47:06 -07:00
Eyob Tefera
bd7d0f864b Revert "Remove unused utility function StringInSplice."
This reverts commit 43b0f2d925.
2020-08-09 07:30:40 +00:00
Jeff Regan
2da8959198 Merge pull request #2818 from monopole/hoserFace
Use yaml.IsYNodeString
2020-08-07 18:21:23 -07:00
Jeff Regan
dc591f0a10 Merge pull request #2817 from monopole/pinToCmdConfigv_0_5_0
Switch to cmd/config v0.5.0
2020-08-07 15:57:19 -07:00
jregan
c94f164e66 Use new 2020-08-07 15:57:00 -07:00
jregan
fb216d8af8 Switch to cmd/config v0.5.0 2020-08-07 15:31:54 -07:00
Jeff Regan
72207bfa04 Merge pull request #2816 from monopole/moveToKyamlv0_5_0
Move to kyaml v0.5.0
2020-08-07 15:13:41 -07:00
jregan
fe3321d710 Move to kyaml v0.5.0 2020-08-07 14:45:56 -07:00
Franz Knipp
ca807019f0 Add full github link to varreference.go
Otherwise, the link is broken on github.io: https://kubernetes-sigs.github.io/kustomize/api-reference/kustomization/vars/
2020-08-07 22:23:18 +02: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
137 changed files with 3417 additions and 2105 deletions

View File

@@ -9,18 +9,15 @@ patch [kubernetes style] API objects. It's like
[`make`], in that what it does is declared in a file,
and it's like [`sed`], in that it emits edited text.
This tool is sponsored by [sig-cli] ([KEP]), and
inspired by [DAM].
This tool is sponsored by [sig-cli] ([KEP]).
- [Installation instructions](https://kubernetes-sigs.github.io/kustomize/installation)
- [General documentation](https://kubernetes-sigs.github.io/kustomize)
- [Examples](examples)
[![Build Status](https://prow.k8s.io/badge.svg?jobs=kustomize-presubmit-master)](https://prow.k8s.io/job-history/kubernetes-jenkins/pr-logs/directory/kustomize-presubmit-master)
[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-sigs/kustomize)](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
Download a binary from the [release page], or see
these [instructions](https://kubernetes-sigs.github.io/kustomize/installation/).
Browse the [docs](https://kubernetes-sigs.github.io/kustomize/) or jump right into the
tested [examples](examples).
## kubectl integration
Since [v1.14][kubectl announcement] the kustomize build system has been included in kubectl.

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

@@ -112,7 +112,7 @@ func (ns Filter) roleBindingHack(obj *yaml.RNode, meta yaml.ResourceMeta) error
// Lookup the namespace field on all elements.
// We should change the fieldspec so this isn't necessary.
obj, err := obj.Pipe(yaml.Lookup(subjectsField))
if err != nil || yaml.IsEmpty(obj) {
if err != nil || yaml.IsMissingOrNull(obj) {
return err
}
@@ -125,7 +125,7 @@ func (ns Filter) roleBindingHack(obj *yaml.RNode, meta yaml.ResourceMeta) error
name, err := o.Pipe(
yaml.Lookup("name"), yaml.Match("default"),
)
if err != nil || yaml.IsEmpty(name) {
if err != nil || yaml.IsMissingOrNull(name) {
return err
}

View File

@@ -66,8 +66,7 @@ func updateNodeValue(node *yaml.Node, newValue interface{}) {
}
func (f Filter) setScalar(node *yaml.RNode) error {
if node.YNode().Kind != yaml.ScalarNode || node.YNode().Tag != yaml.NodeTagString {
// Only process string values
if !yaml.IsYNodeString(node.YNode()) {
return nil
}
v := expansion2.Expand(node.YNode().Value, f.MappingFunc)
@@ -78,11 +77,10 @@ func (f Filter) setScalar(node *yaml.RNode) error {
func (f Filter) setMap(node *yaml.RNode) error {
contents := node.YNode().Content
for i := 0; i < len(contents); i += 2 {
if contents[i].Kind != yaml.ScalarNode || contents[i].Tag != yaml.NodeTagString {
if !yaml.IsYNodeString(contents[i]) {
return fmt.Errorf("invalid map key: %s, type: %s", contents[i].Value, contents[i].Tag)
}
if contents[i+1].Kind != yaml.ScalarNode || contents[i+1].Tag != yaml.NodeTagString {
// value is not a string
if !yaml.IsYNodeString(contents[i+1]) {
continue
}
newValue := expansion2.Expand(contents[i+1].Value, f.MappingFunc)
@@ -93,8 +91,7 @@ func (f Filter) setMap(node *yaml.RNode) error {
func (f Filter) setSeq(node *yaml.RNode) error {
for _, item := range node.YNode().Content {
if item.Kind != yaml.ScalarNode || item.Tag != yaml.NodeTagString {
// value is not a string
if !yaml.IsYNodeString(item) {
return fmt.Errorf("invalid value type expect a string")
}
newValue := expansion2.Expand(item.Value, f.MappingFunc)

View File

@@ -16,8 +16,8 @@ require (
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.4.2
sigs.k8s.io/kustomize/kyaml v0.5.0
sigs.k8s.io/yaml v1.2.0
)
replace sigs.k8s.io/kustomize/kyaml v0.4.2 => ../kyaml
replace sigs.k8s.io/kustomize/kyaml v0.5.0 => ../kyaml

View File

@@ -41,32 +41,66 @@ type Loader interface {
// Kunstructured allows manipulation of k8s objects
// that do not have Golang structs.
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

@@ -413,8 +413,6 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -503,8 +501,8 @@ 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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
@@ -525,8 +523,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.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/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

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

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

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

@@ -8,7 +8,7 @@ import (
"sigs.k8s.io/kustomize/api/builtins"
"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"
@@ -49,11 +49,11 @@ 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)
resourceFactory := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl())
resmapFactory := resmap.NewFactory(
resourceFactory,
merge.NewMerginator(resourceFactory))
lr := fLdr.RestrictionNone
if b.options.LoadRestrictions == types.LoadRestrictionsRootOnly {
lr = fLdr.RestrictionRootOnly
@@ -66,9 +66,8 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
kt := target.NewKustTarget(
ldr,
validator.NewKustValidator(),
rf,
pf,
pLdr.NewLoader(b.options.PluginConfig, rf),
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
envFrom:
- configMapRef:
name: mysql
name: handler
`)
}

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

@@ -14,12 +14,12 @@ import (
// Factory makes instances of ResMap.
type Factory struct {
resF *resource.Factory
tf PatchFactory
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 +87,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 +112,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 +122,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)

13
api/resmap/merginator.go Normal file
View File

@@ -0,0 +1,13 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resmap
import "sigs.k8s.io/kustomize/api/resource"
// Merginator merges resources.
type Merginator interface {
// Merge creates a new ResMap by merging incoming resources.
// Error if conflict found.
Merge([]*resource.Resource) (ResMap, error)
}

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

@@ -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 {
@@ -384,7 +305,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 +315,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 +340,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 +350,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 +358,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

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

@@ -14,8 +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.17.0
sigs.k8s.io/kustomize/kyaml v0.4.2
sigs.k8s.io/cli-utils v0.19.0
sigs.k8s.io/kustomize/kyaml v0.6.0
)
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=
@@ -614,8 +615,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/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.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=

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

@@ -177,13 +177,13 @@ func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
}
if r.WrapKind != "" {
if kind := rw.FunctionConfig.Field("kind"); !yaml.IsFieldEmpty(kind) {
if kind := rw.FunctionConfig.Field("kind"); !kind.IsNilOrEmpty() {
kind.Value.YNode().Value = r.WrapKind
}
rw.WrappingKind = r.WrapKind
}
if r.WrapVersion != "" {
if version := rw.FunctionConfig.Field("apiVersion"); !yaml.IsFieldEmpty(version) {
if version := rw.FunctionConfig.Field("apiVersion"); !version.IsNilOrEmpty() {
version.Value.YNode().Value = r.WrapVersion
}
rw.WrappingAPIVersion = r.WrapVersion

View File

@@ -5,6 +5,6 @@ go 1.14
require (
k8s.io/apimachinery v0.18.3
sigs.k8s.io/application v0.8.2
sigs.k8s.io/kustomize/kyaml v0.4.2
sigs.k8s.io/kustomize/kyaml v0.5.0
sigs.k8s.io/yaml v1.2.0
)

View File

@@ -406,9 +406,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/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=
@@ -511,8 +510,8 @@ 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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -559,8 +558,8 @@ sigs.k8s.io/application v0.8.2 h1:XB7C33f7eW+1MbCJXoZa0+nP+R+S/VbAvYfCd3ufP1I=
sigs.k8s.io/application v0.8.2/go.mod h1:Mv+ht9RE/QNtITYCzRbt3XTIN6t6so6cInmiyg6wOIg=
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/kyaml v0.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=

View File

@@ -2,4 +2,4 @@ module sigs.k8s.io/kustomize/functions/examples/injection-tshirt-sizes
go 1.14
require sigs.k8s.io/kustomize/kyaml v0.4.2
require sigs.k8s.io/kustomize/kyaml v0.5.0

View File

@@ -169,6 +169,8 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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=
@@ -212,9 +214,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -258,8 +259,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/kustomize/kyaml v0.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/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=

View File

@@ -2,4 +2,4 @@ module sigs.k8s.io/kustomize/functions/examples/template-go-nginx
go 1.14
require sigs.k8s.io/kustomize/kyaml v0.4.2
require sigs.k8s.io/kustomize/kyaml v0.5.0

View File

@@ -170,6 +170,8 @@ github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHN
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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=
@@ -213,9 +215,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -259,8 +260,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/kustomize/kyaml v0.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/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=

View File

@@ -4,5 +4,5 @@ go 1.14
require (
github.com/instrumenta/kubeval v0.0.0-20190918223246-8d013ec9fc56
sigs.k8s.io/kustomize/kyaml v0.4.2
sigs.k8s.io/kustomize/kyaml v0.5.0
)

View File

@@ -189,6 +189,8 @@ github.com/spf13/pflag v0.0.0-20180821114517-d929dcbb1086 h1:iU+nPfqRqK8ShQqnpZL
github.com/spf13/pflag v0.0.0-20180821114517-d929dcbb1086/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.1.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
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=
@@ -239,9 +241,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -289,10 +290,10 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/kustomize/kyaml v0.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/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/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -2,4 +2,4 @@ module sigs.k8s.io/kustomize/functions/examples/validator-resource-requests
go 1.14
require sigs.k8s.io/kustomize/kyaml v0.4.2
require sigs.k8s.io/kustomize/kyaml v0.5.0

View File

@@ -164,6 +164,7 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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=
@@ -207,9 +208,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -253,8 +253,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/kustomize/kyaml v0.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/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=

View File

@@ -3,18 +3,24 @@ module sigs.k8s.io/kustomize/kustomize/v3
go 1.14
require (
github.com/google/go-cmp v0.3.0
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
k8s.io/client-go v0.17.3
sigs.k8s.io/kustomize/api v0.5.1
sigs.k8s.io/kustomize/cmd/config v0.2.0
sigs.k8s.io/kustomize/cmd/config v0.5.0
sigs.k8s.io/yaml v1.2.0
)
replace sigs.k8s.io/kustomize/api v0.5.1 => ../api
replace (
sigs.k8s.io/kustomize/api v0.5.1 => ../api
sigs.k8s.io/kustomize/cmd/config v0.5.0 => ../cmd/config
sigs.k8s.io/kustomize/kyaml => ../kyaml
)
exclude (
github.com/russross/blackfriday v2.0.0+incompatible
sigs.k8s.io/kustomize/api v0.2.0
sigs.k8s.io/kustomize/cmd/config v0.2.0
)

View File

@@ -458,6 +458,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=
@@ -558,9 +559,8 @@ golang.org/x/net v0.0.0-20190812203447-cdfb69ac37fc/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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=
@@ -670,12 +670,10 @@ gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=
@@ -744,18 +742,12 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
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/cli-utils v0.12.0 h1:+CvYwQAEtKvcx/NaUVF9rDKvY91VwJj+i7D2lWBMYc0=
sigs.k8s.io/cli-utils v0.12.0/go.mod h1:H35YA5iJIM7EVNgqDTjX2dgt4wE23zmnXOTSTlyD+PE=
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/cmd/config v0.2.0 h1:VNAWKb1JLl7dFjMAD5MwdZpGjreN3qL63C8DKtsUYck=
sigs.k8s.io/kustomize/cmd/config v0.2.0/go.mod h1:oXzY7QJS6JlmWgusEjra2O3cW7GSIICXa59/3DvjBfE=
sigs.k8s.io/kustomize/kyaml v0.1.4/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
sigs.k8s.io/kustomize/kyaml v0.2.0/go.mod h1:72/rLkSi+L/pHM1oCjwrf3ClU+tH5kZQvvdLSqIHwWU=
sigs.k8s.io/kustomize/kyaml v0.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
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/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=

View File

@@ -170,6 +170,7 @@ func (mf *kustomizationFile) Write(kustomization *types.Kustomization) error {
if kustomization == nil {
return errors.New("util: kustomization file arg is nil")
}
kustomization.FixKustomizationPreMarshalling()
data, err := mf.marshal(kustomization)
if err != nil {
return err

View File

@@ -8,6 +8,8 @@ import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
@@ -158,8 +160,8 @@ patchesStrategicMerge:
}
bytes, _ := fSys.ReadFile(mf.path)
if !reflect.DeepEqual(kustomizationContentWithComments, bytes) {
t.Fatal("written kustomization with comments is not the same as original one")
if diff := cmp.Diff(kustomizationContentWithComments, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
@@ -252,10 +254,8 @@ generatorOptions:
}
bytes, _ := fSys.ReadFile(mf.path)
if string(expected) != string(bytes) {
t.Fatalf(
"expected =\n%s\n\nactual =\n%s\n",
string(expected), string(bytes))
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
@@ -290,10 +290,8 @@ kind: Kustomization
}
bytes, _ := fSys.ReadFile(mf.path)
if string(expected) != string(bytes) {
t.Fatalf(
"expected =\n%s\n\nactual =\n%s\n",
string(expected), string(bytes))
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
@@ -335,10 +333,105 @@ kind: Kustomization
}
bytes, _ := fSys.ReadFile(mf.path)
if string(expected) != string(bytes) {
t.Fatalf(
"expected =\n%s\n\nactual =\n%s\n",
string(expected), string(bytes))
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
func TestRenameAndKeepOutdatedPatchesField(t *testing.T) {
kustomizationContentWithOutdatedPatchesFieldTitle := []byte(`
patchesJson6902:
- path: patch1.yaml
target:
kind: Deployment
patches:
- path: patch2.yaml
target:
kind: Deployment
- path: patch3.yaml
target:
kind: Service
`)
expected := []byte(`
patches:
- path: patch2.yaml
target:
kind: Deployment
- path: patch3.yaml
target:
kind: Service
- path: patch1.yaml
target:
kind: Deployment
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`)
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedPatchesFieldTitle)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
func TestFixOutdatedPatchesFieldTitle(t *testing.T) {
kustomizationContentWithOutdatedPatchesFieldTitle := []byte(`
patchesJson6902:
- path: patch1.yaml
target:
kind: Service
- path: patch2.yaml
target:
group: apps
kind: Deployment
version: v1
`)
expected := []byte(`
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patch1.yaml
target:
kind: Service
- path: patch2.yaml
target:
group: apps
kind: Deployment
version: v1
`)
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedPatchesFieldTitle)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}

View File

@@ -123,15 +123,7 @@ import (
//    ├── deployment_foo.yaml
//    └── service_bar.yaml
type Filter struct {
// Image is the container image to use to create a container.
Image string `yaml:"image,omitempty"`
// Network is the container network to use.
Network string `yaml:"network,omitempty"`
// StorageMounts is a list of storage options that the container will have mounted.
StorageMounts []runtimeutil.StorageMount `yaml:"mounts,omitempty"`
runtimeutil.ContainerSpec `json:",inline" yaml:",inline"`
Exec runtimeexec.Filter
}
@@ -171,17 +163,16 @@ func (c *Filter) getCommand() (string, []string) {
// was run from the cli.
network := "none"
if c.Network != "" {
network = c.Network
if c.Network.Name != "" {
network = c.Network.Name
}
args := []string{"run",
"--rm", // delete the container afterward
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", // attach stdin, stdout, stderr
"--network", network,
// added security options
"--user", "nobody", // run as nobody
"--user", c.User.String(),
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
// note: don't make fs readonly because things like heredoc rely on writing tmp files
}

View File

@@ -38,7 +38,12 @@ metadata:
"--user", "nobody",
"--security-opt=no-new-privileges",
},
instance: Filter{Image: "example.com:version"},
instance: Filter{
ContainerSpec: runtimeutil.ContainerSpec{
Image: "example.com:version",
User: "nobody",
},
},
},
{
@@ -56,7 +61,15 @@ metadata:
"--user", "nobody",
"--security-opt=no-new-privileges",
},
instance: Filter{Image: "example.com:version", Network: "test-1"},
instance: Filter{
ContainerSpec: runtimeutil.ContainerSpec{
Image: "example.com:version",
Network: runtimeutil.ContainerNetwork{
Name: "test-1",
},
User: "nobody",
},
},
},
{
@@ -79,12 +92,37 @@ metadata:
"--mount", fmt.Sprintf("type=%s,source=%s,target=%s,readonly", "tmpfs", "", "/local/"),
},
instance: Filter{
Image: "example.com:version",
StorageMounts: []runtimeutil.StorageMount{
{MountType: "bind", Src: "/mount/path", DstPath: "/local/"},
{MountType: "bind", Src: "/mount/pathrw", DstPath: "/localrw/", ReadWriteMode: true},
{MountType: "volume", Src: "myvol", DstPath: "/local/"},
{MountType: "tmpfs", Src: "", DstPath: "/local/"},
ContainerSpec: runtimeutil.ContainerSpec{
Image: "example.com:version",
StorageMounts: []runtimeutil.StorageMount{
{MountType: "bind", Src: "/mount/path", DstPath: "/local/"},
{MountType: "bind", Src: "/mount/pathrw", DstPath: "/localrw/", ReadWriteMode: true},
{MountType: "volume", Src: "myvol", DstPath: "/local/"},
{MountType: "tmpfs", Src: "", DstPath: "/local/"},
},
User: "nobody",
},
},
},
{
name: "root user",
functionConfig: `apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
expectedArgs: []string{
"run",
"--rm",
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
"--network", "none",
"--user", "root",
"--security-opt=no-new-privileges",
},
instance: Filter{
ContainerSpec: runtimeutil.ContainerSpec{
Image: "example.com:version",
User: "root",
},
},
},
@@ -183,7 +221,7 @@ metadata:
}
}
func TestFilter_String(t *testing.T) {
instance := Filter{Image: "foo"}
instance := Filter{ContainerSpec: runtimeutil.ContainerSpec{Image: "foo"}}
if !assert.Equal(t, "foo", instance.String()) {
t.FailNow()
}
@@ -214,7 +252,7 @@ func TestFilter_ExitCode(t *testing.T) {
func TestIgnoreEnv(t *testing.T) {
os.Setenv(tmpDirEnvKey, "")
fltr := Filter{Image: "example.com:version"}
fltr := Filter{ContainerSpec: runtimeutil.ContainerSpec{Image: "example.com:version"}}
_, args := fltr.getCommand()
for _, arg := range args {
if arg == tmpDirEnvKey {

View File

@@ -18,11 +18,23 @@ const (
var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotationKey}
// ContainerUser is a type for username/uid used in container
type ContainerUser string
func (u *ContainerUser) String() string {
return string(*u)
}
func (u *ContainerUser) IsEmpty() bool {
return string(*u) == ""
}
const (
UserNobody ContainerUser = "nobody"
)
// FunctionSpec defines a spec for running a function
type FunctionSpec struct {
// Network is the name of the network to use from a container
Network string `json:"network,omitempty" yaml:"network,omitempty"`
DeferFailure bool `json:"deferFailure,omitempty" yaml:"deferFailure,omitempty"`
// Container is the spec for running a function as a container
@@ -52,12 +64,18 @@ type ContainerSpec struct {
// Mounts are the storage or directories to mount into the container
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
// User is the username/uid that application runs as in continer
User ContainerUser `json:"user,omitempty" yaml:"user,omitempty"`
}
// ContainerNetwork
type ContainerNetwork struct {
// Required specifies that function requires a network
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
// Name is the name of the network to use from a container
Name string `json:"name,omitempty" yaml:"name,omitempty"`
}
// StarlarkSpec defines how to run a function as a starlark program
@@ -110,7 +128,7 @@ func GetFunctionSpec(n *yaml.RNode) *FunctionSpec {
}
if fn := getFunctionSpecFromAnnotation(n, meta); fn != nil {
fn.Network = ""
fn.Container.Network.Name = ""
fn.StorageMounts = []StorageMount{}
return fn
}
@@ -138,7 +156,7 @@ func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) *Funct
}
}
n, err := n.Pipe(yaml.Lookup("metadata", "configFn"))
if err != nil || yaml.IsEmpty(n) {
if err != nil || yaml.IsMissingOrNull(n) {
return nil
}
s, err := n.String()

View File

@@ -185,6 +185,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
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 v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

View File

@@ -80,6 +80,15 @@ type Pipeline struct {
// Execute executes each step in the sequence, returning immediately after encountering
// any error as part of the Pipeline.
func (p Pipeline) Execute() error {
return p.ExecuteWithCallback(nil)
}
// PipelineExecuteCallbackFunc defines a callback function that will be called each time a step in the pipeline succeeds.
type PipelineExecuteCallbackFunc = func(op Filter)
// ExecuteWithCallback executes each step in the sequence, returning immediately after encountering
// any error as part of the Pipeline. The callback will be called each time a step succeeds.
func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) error {
var result []*yaml.RNode
// read from the inputs
@@ -90,16 +99,18 @@ func (p Pipeline) Execute() error {
}
result = append(result, nodes...)
}
if len(result) == 0 {
// no inputs to operate on
return nil
}
// apply operations
var err error
for i := range p.Filters {
op := p.Filters[i]
if callback != nil {
callback(op)
}
result, err = op.Filter(result)
// TODO (issue 2872): This len(result) == 0 should be removed and empty result list should be
// handled by outputs. However currently some writer like LocalPackageReadWriter
// will clear the output directory and which will cause unpredictable results
if len(result) == 0 || err != nil {
return errors.Wrap(err)
}

View File

@@ -4,9 +4,16 @@
package kio_test
import (
"bytes"
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"sigs.k8s.io/kustomize/kyaml/yaml"
. "sigs.k8s.io/kustomize/kyaml/kio"
)
@@ -23,6 +30,122 @@ func TestPipe(t *testing.T) {
}
}
func TestSlice_Write(t *testing.T) {
type mockCallback struct {
mock.Mock
}
func (c *mockCallback) Callback(op Filter) {
c.Called(op)
}
func TestPipelineWithCallback(t *testing.T) {
input := ResourceNodeSlice{yaml.MakeNullNode()}
noopFilter1 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return nodes, nil
}
noopFilter2 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return nodes, nil
}
filters := []Filter{
FilterFunc(noopFilter1),
FilterFunc(noopFilter2),
}
p := Pipeline{
Inputs: []Reader{input},
Filters: filters,
Outputs: []Writer{},
}
callback := mockCallback{}
// setup expectations. `Times` means the function is called no more than `times`.
callback.On("Callback", mock.Anything).Times(len(filters))
err := p.ExecuteWithCallback(callback.Callback)
if !assert.NoError(t, err) {
assert.FailNow(t, err.Error())
}
callback.AssertNumberOfCalls(t, "Callback", len(filters))
// assert filters are called in the order they are defined.
for i, filter := range filters {
assert.Equal(
t,
reflect.ValueOf(callback.Calls[i].Arguments[0]).Pointer(),
reflect.ValueOf(filter).Pointer(),
)
}
}
func TestEmptyInput(t *testing.T) {
actual := &bytes.Buffer{}
output := ByteWriter{
Sort: true,
WrappingKind: ResourceListKind,
WrappingAPIVersion: ResourceListAPIVersion,
}
output.Writer = actual
p := Pipeline{
Outputs: []Writer{output},
}
err := p.Execute()
if err != nil {
t.Fatal(err)
}
expected := `
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items: []
`
if !assert.Equal(t,
strings.TrimSpace(expected), strings.TrimSpace(actual.String())) {
t.FailNow()
}
}
func TestEmptyInputWithFilter(t *testing.T) {
actual := &bytes.Buffer{}
output := ByteWriter{
Sort: true,
WrappingKind: ResourceListKind,
WrappingAPIVersion: ResourceListAPIVersion,
}
output.Writer = actual
filters := []Filter{
FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
nodes = append(nodes, yaml.NewMapRNode(&map[string]string{
"foo": "bar",
}))
return nodes, nil
}),
FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return nodes, nil }),
}
p := Pipeline{
Outputs: []Writer{output},
Filters: filters,
}
err := p.Execute()
if err != nil {
t.Fatal(err)
}
expected := `
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- foo: bar
`
if !assert.Equal(t,
strings.TrimSpace(expected), strings.TrimSpace(actual.String())) {
t.FailNow()
}
}

View File

@@ -275,40 +275,67 @@ func TestCreatePathAnnotationValue(t *testing.T) {
}{
{
`dir`,
yaml.ResourceMeta{Kind: "foo",
APIVersion: "apps/v1",
ObjectMeta: yaml.ObjectMeta{Name: "bar", Namespace: "baz"},
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{
Name: "bar", Namespace: "baz",
},
},
},
`dir/baz/foo_bar.yaml`, `with namespace`,
},
{
``,
yaml.ResourceMeta{Kind: "foo",
APIVersion: "apps/v1",
ObjectMeta: yaml.ObjectMeta{Name: "bar", Namespace: "baz"},
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{
Name: "bar", Namespace: "baz",
},
},
},
`baz/foo_bar.yaml`, `without dir`,
},
{
`dir`,
yaml.ResourceMeta{Kind: "foo",
APIVersion: "apps/v1",
ObjectMeta: yaml.ObjectMeta{Name: "bar"},
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{Name: "bar"},
},
},
`dir/foo_bar.yaml`, `without namespace`,
},
{
``,
yaml.ResourceMeta{Kind: "foo",
APIVersion: "apps/v1",
ObjectMeta: yaml.ObjectMeta{Name: "bar"},
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{
NameMeta: yaml.NameMeta{Name: "bar"},
},
},
`foo_bar.yaml`, `without namespace or dir`,
},
{
``,
yaml.ResourceMeta{Kind: "foo",
APIVersion: "apps/v1",
yaml.ResourceMeta{
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "foo",
},
ObjectMeta: yaml.ObjectMeta{},
},
`foo_.yaml`, `without namespace, dir or name`,
@@ -316,7 +343,9 @@ func TestCreatePathAnnotationValue(t *testing.T) {
{
``,
yaml.ResourceMeta{
APIVersion: "apps/v1",
TypeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
},
ObjectMeta: yaml.ObjectMeta{},
},
`_.yaml`, `without any`,

View File

@@ -238,10 +238,10 @@ func ownerToString(node *yaml.RNode) (string, error) {
owner := elements[0]
var kind, name string
if value := owner.Field("kind"); !yaml.IsFieldEmpty(value) {
if value := owner.Field("kind"); !value.IsNilOrEmpty() {
kind = value.Value.YNode().Value
}
if value := owner.Field("name"); !yaml.IsFieldEmpty(value) {
if value := owner.Field("name"); !value.IsNilOrEmpty() {
name = value.Value.YNode().Value
}

View File

@@ -36,7 +36,7 @@ type ResourceSchema struct {
}
// IsEmpty returns true if the ResourceSchema is empty
func (rs *ResourceSchema) IsEmpty() bool {
func (rs *ResourceSchema) IsMissingOrNull() bool {
if rs == nil || rs.Schema == nil {
return true
}
@@ -84,7 +84,7 @@ func AddSchemaFromFileUsingField(path, field string) error {
if field != "" {
// get the field containing the openAPI
m := y.Field(field)
if yaml.IsFieldEmpty(m) {
if m.IsNilOrEmpty() {
// doesn't contain openAPI definitions
return nil
}

View File

@@ -74,6 +74,12 @@ type RunFns struct {
// ResultsDir is where to write each functions results
ResultsDir string
// LogSteps enables logging the function that is running.
LogSteps bool
// LogWriter can be set to write the logs to LogWriter rather than stderr if LogSteps is enabled.
LogWriter io.Writer
// resultsCount is used to generate the results filename for each container
resultsCount uint32
@@ -81,6 +87,9 @@ type RunFns struct {
// this is a variable so it can be mocked in tests
functionFilterProvider func(
filter runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter, error)
// User username used to run the application in container,
User runtimeutil.ContainerUser
}
// Execute runs the command
@@ -169,8 +178,33 @@ func (r RunFns) runFunctions(
// the output is nil (reading from Input)
outputs = append(outputs, kio.ByteWriter{Writer: r.Output})
}
err := kio.Pipeline{
Inputs: []kio.Reader{input}, Filters: fltrs, Outputs: outputs}.Execute()
var err error
pipeline := kio.Pipeline{
Inputs: []kio.Reader{input},
Filters: fltrs,
Outputs: outputs,
}
if r.LogSteps {
err = pipeline.ExecuteWithCallback(func(op kio.Filter) {
var identifier string
switch filter := op.(type) {
case *container.Filter:
identifier = filter.Image
case *exec.Filter:
identifier = filter.Path
case *starlark.Filter:
identifier = filter.String()
default:
identifier = "unknown-type function"
}
_, _ = fmt.Fprintf(r.LogWriter, "Running %s\n", identifier)
})
} else {
err = pipeline.Execute()
}
if err != nil {
return err
}
@@ -246,8 +280,17 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
// TODO(eddiezane): Provide error info about which function needs the network
return fltrs, errors.Errorf("network required but not enabled with --network")
}
spec.Network = r.NetworkName
spec.Container.Network.Name = r.NetworkName
}
// command line username has higher priority
if r.User != "" {
spec.Container.User = r.User
}
// default user is nobody
if spec.Container.User.IsEmpty() {
spec.Container.User = runtimeutil.UserNobody
}
c, err := r.functionFilterProvider(*spec, api)
if err != nil {
return nil, err
@@ -333,6 +376,11 @@ func (r *RunFns) init() {
if r.functionFilterProvider == nil {
r.functionFilterProvider = r.ffp
}
// if LogSteps is enabled and LogWriter is not specified, use stderr
if r.LogSteps && r.LogWriter == nil {
r.LogWriter = os.Stderr
}
}
// ffp provides function filters
@@ -346,9 +394,12 @@ func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter
if !r.DisableContainers && spec.Container.Image != "" {
// TODO: Add a test for this behavior
cf := &container.Filter{
Image: spec.Container.Image,
Network: spec.Network,
StorageMounts: r.StorageMounts,
ContainerSpec: runtimeutil.ContainerSpec{
Image: spec.Container.Image,
Network: spec.Container.Network,
StorageMounts: r.StorageMounts,
User: spec.Container.User,
},
}
cf.Exec.FunctionConfig = api
cf.Exec.GlobalScope = r.GlobalScope

View File

@@ -14,6 +14,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/container"
@@ -59,7 +60,7 @@ kind:
return
}
filter, _ := instance.functionFilterProvider(spec, api)
cf := &container.Filter{Image: "example.com:version"}
cf := &container.Filter{ContainerSpec: runtimeutil.ContainerSpec{Image: "example.com:version"}}
cf.Exec.FunctionConfig = api
assert.Equal(t, cf, filter)
}
@@ -89,7 +90,7 @@ kind:
return
}
filter, _ := instance.functionFilterProvider(spec, api)
cf := &container.Filter{Image: "example.com:version"}
cf := &container.Filter{ContainerSpec: runtimeutil.ContainerSpec{Image: "example.com:version"}}
cf.Exec.FunctionConfig = api
cf.Exec.GlobalScope = true
assert.Equal(t, cf, filter)
@@ -906,6 +907,36 @@ func TestCmd_Execute_setInput(t *testing.T) {
assert.Contains(t, string(b), "kind: StatefulSet")
}
// TestCmd_Execute_enableLogSteps tests the execution of a filter with LogSteps enabled.
func TestCmd_Execute_enableLogSteps(t *testing.T) {
dir := setupTest(t)
defer os.RemoveAll(dir)
// write a test filter to the directory of configuration
if !assert.NoError(t, ioutil.WriteFile(
filepath.Join(dir, "filter.yaml"), []byte(ValueReplacerYAMLData), 0600)) {
return
}
logs := &bytes.Buffer{}
instance := RunFns{
Path: dir,
functionFilterProvider: getFilterProvider(t),
LogSteps: true,
LogWriter: logs,
}
if !assert.NoError(t, instance.Execute()) {
t.FailNow()
}
b, err := ioutil.ReadFile(
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
if !assert.NoError(t, err) {
t.FailNow()
}
assert.Contains(t, string(b), "kind: StatefulSet")
assert.Equal(t, "Running unknown-type function\n", logs.String())
}
// setupTest initializes a temp test directory containing test data
func setupTest(t *testing.T) string {
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")

View File

@@ -154,6 +154,40 @@ metadata:
replicas: 3
spec:
replicas: 3 # {"$openapi":"replicas"}
`,
},
{
name: "add-field-inside-sequence",
add: Add{
FieldValue: "/usr/share/nginx",
FieldName: "spec.containers.volumeMounts.mountPath",
Ref: "#/definitions/io.k8s.cli.setters.mountPath",
},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nginx
mountPath: /usr/share/nginx
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nginx
mountPath: /usr/share/nginx # {"$openapi":"mountPath"}
`,
},
{

View File

@@ -13,23 +13,17 @@ import (
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Delete delete setter or substitution references from resource fields.
// Requires that FieldName have been set.
// Delete delete openAPI definition references from resource fields.
type Delete struct {
// Name is the name of the openAPI definition to delete.
Name string
// FieldName if delete the OpenAPI reference to fields with this name or path
// FieldName may be the full name of the field, full path to the field, or the path suffix.
// e.g. all of the following would match spec.template.spec.containers.image --
// [image, containers.image, spec.containers.image, template.spec.containers.image,
// spec.template.spec.containers.image]
FieldName string
// DefinitionPrefix is the prefix of the OpenAPI definition type
DefinitionPrefix string
}
// Filter implements yaml.Filter
func (d *Delete) Filter(object *yaml.RNode) (*yaml.RNode, error) {
if d.FieldName == "" {
return nil, errors.Errorf("must specify fieldName")
}
return object, accept(d, object)
}
@@ -38,31 +32,40 @@ func (d *Delete) visitSequence(_ *yaml.RNode, _ string, _ *openapi.ResourceSchem
return nil
}
func (d *Delete) visitMapping(_ *yaml.RNode, _ string, _ *openapi.ResourceSchema) error {
// no-op
func (d *Delete) visitMapping(object *yaml.RNode, _ string, _ *openapi.ResourceSchema) error {
fieldRNodes, err := object.FieldRNodes()
if err != nil {
return err
}
// for each of the field node key visit it as scalar to delete the array setter comment
for _, fieldRNode := range fieldRNodes {
err := d.visitScalar(fieldRNode, "", nil, nil)
if err != nil {
return err
}
}
return nil
}
// visitScalar implements visitor
// visitScalar will remove the reference on each scalar field whose name matches.
func (d *Delete) visitScalar(object *yaml.RNode, p string, _, _ *openapi.ResourceSchema) error {
// check if the field matches
if d.FieldName != "" && !strings.HasSuffix(p, d.FieldName) {
return nil
}
func (d *Delete) visitScalar(object *yaml.RNode, _ string, _, _ *openapi.ResourceSchema) error {
// read the field metadata
fm := fieldmeta.FieldMeta{}
if err := fm.Read(object); err != nil {
return err
}
// remove the ref on the metadata
fm.Schema.Ref = spec.Ref{}
// Delete the reference iff the ref string matches with DefinitionPrefix
if strings.HasSuffix(fm.Schema.Ref.String(), d.DefinitionPrefix+d.Name) {
// remove the ref on the metadata
fm.Schema.Ref = spec.Ref{}
// write the field metadata
if err := fm.Write(object); err != nil {
return err
// write the field metadata
if err := fm.Write(object); err != nil {
return err
}
}
return nil
@@ -70,16 +73,19 @@ func (d *Delete) visitScalar(object *yaml.RNode, p string, _, _ *openapi.Resourc
// DeleterDefinition may be used to update a files OpenAPI definitions with a new setter.
type DeleterDefinition struct {
// Name is the name of the setter to create or update.
// Name is the name of the openAPI definition to delete.
Name string `yaml:"name"`
// DefinitionPrefix is the prefix of the OpenAPI definition type
DefinitionPrefix string `yaml:"definitionPrefix"`
}
func (dd DeleterDefinition) DeleteFromFile(path string) error {
return yaml.UpdateFile(dd, path)
}
// SubstReferringSetter check if the setter used in substitution and return the substitution name if true
func SubstReferringSetter(definitions *yaml.RNode, key string) string {
// SubstReferringDefinition check if the definition used in substitution and return the substitution name if true
func SubstReferringDefinition(definitions *yaml.RNode, key string) string {
fieldNames, err := definitions.Fields()
if err != nil {
return ""
@@ -115,7 +121,18 @@ func SubstReferringSetter(definitions *yaml.RNode, key string) string {
}
func (dd DeleterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
key := fieldmeta.SetterDefinitionPrefix + dd.Name
key := dd.DefinitionPrefix + dd.Name
var defType string
switch dd.DefinitionPrefix {
case fieldmeta.SubstitutionDefinitionPrefix:
defType = "substitution"
case fieldmeta.SetterDefinitionPrefix:
defType = "setter"
default:
return nil, errors.Errorf("the input delete definitionPrefix does't match any of openAPI definitions, "+
"allowed values [%s, %s]", fieldmeta.SetterDefinitionPrefix, fieldmeta.SubstitutionDefinitionPrefix)
}
definitions, err := object.Pipe(yaml.Lookup(openapi.SupplementaryOpenAPIFieldName, "definitions"))
if err != nil || definitions == nil {
@@ -123,13 +140,13 @@ func (dd DeleterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
}
// return error if the setter to be deleted doesn't exist
if definitions.Field(key) == nil {
return nil, errors.Errorf("setter does not exist")
return nil, errors.Errorf("%s with name %s does not exist", defType, dd.Name)
}
subst := SubstReferringSetter(definitions, key)
subst := SubstReferringDefinition(definitions, key)
if subst != "" {
return nil, errors.Errorf("setter is used in substitution %s, please delete the substitution first", subst)
return nil, errors.Errorf("%s is used in substitution %s, please delete the parent substitution first", defType, subst)
}
_, err = definitions.Pipe(yaml.FieldClearer{Name: key})

View File

@@ -7,116 +7,12 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
)
func TestDelete_Filter(t *testing.T) {
var tests = []struct {
name string
description string
setter string
input string
expectedOutput string
}{
{
name: "delete-replicas",
setter: "replicas",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
replicas: 3 # {"$openapi":"replicas"}
spec:
replicas: 3 # {"$openapi":"replicas"}
`,
expectedOutput: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
replicas: 3
spec:
replicas: 3
`,
},
{
name: "delete-foo-annotation",
setter: "foo",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
foo: 3 # {"$openapi":"foo"}
`,
expectedOutput: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
foo: 3
`,
},
{
name: "delete-replicas-enum",
setter: "replicas",
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1 # {"$openapi":"replicas"}
`,
expectedOutput: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// parse the input to be modified
r, err := yaml.Parse(test.input)
if !assert.NoError(t, err) {
t.FailNow()
}
// invoke the delete
instance := &Delete{FieldName: test.setter}
result, err := instance.Filter(r)
if !assert.NoError(t, err) {
t.FailNow()
}
// compare the actual and expected output
actual, err := result.String()
if !assert.NoError(t, err) {
t.FailNow()
}
actual = strings.TrimSpace(actual)
expected := strings.TrimSpace(test.expectedOutput)
if !assert.Equal(t, expected, actual) {
t.FailNow()
}
})
}
}
var resourcefile2 = `apiVersion: resource.dev/v1alpha1
kind: resourcefile
metadata:
@@ -154,7 +50,8 @@ func TestDelete_Filter2(t *testing.T) {
//add a deleter definition
dd := DeleterDefinition{
Name: "image",
Name: "image",
DefinitionPrefix: fieldmeta.SetterDefinitionPrefix,
}
err = dd.DeleteFromFile(path)

View File

@@ -53,7 +53,7 @@ func (l *List) listSetters(object *yaml.RNode, resourcePath string) error {
if err != nil {
return err
}
if yaml.IsEmpty(def) {
if yaml.IsMissingOrNull(def) {
return nil
}
@@ -73,7 +73,7 @@ func (l *List) listSetters(object *yaml.RNode, resourcePath string) error {
if err != nil {
return err
}
if yaml.IsEmpty(setterNode) {
if yaml.IsMissingOrNull(setterNode) {
// has the setter prefix, but missing the setter extension
return errors.Errorf("missing x-k8s-cli.setter for %s", key)
}
@@ -126,7 +126,7 @@ func (l *List) listSubst(object *yaml.RNode) error {
if err != nil {
return err
}
if yaml.IsEmpty(def) {
if yaml.IsMissingOrNull(def) {
return nil
}
@@ -146,7 +146,7 @@ func (l *List) listSubst(object *yaml.RNode) error {
if err != nil {
return err
}
if yaml.IsEmpty(substNode) {
if yaml.IsMissingOrNull(substNode) {
// has the substitution prefix, but missing the setter extension
return errors.Errorf("missing x-k8s-cli.substitution for %s", key)
}

View File

@@ -45,7 +45,7 @@ func (s *Set) isMatch(name string) bool {
return s.SetAll || s.Name == name
}
func (s *Set) visitMapping(object *yaml.RNode, p string, _ *openapi.ResourceSchema) error {
func (s *Set) visitMapping(_ *yaml.RNode, p string, _ *openapi.ResourceSchema) error {
return nil
}

View File

@@ -10,6 +10,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
var openAPIFile = `
@@ -25,7 +27,6 @@ openAPI:
setter:
name: tag
value: "sometag"
`
var resourceFile = `
@@ -40,18 +41,20 @@ spec:
`
func TestDeleterCreator_Delete(t *testing.T) {
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
openAPI, err := ioutil.TempFile("", "openAPI.yaml")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.Remove(openAPI.Name())
//write openapi to temp dir
// write openapi to temp dir
err = ioutil.WriteFile(openAPI.Name(), []byte(openAPIFile), 0666)
if !assert.NoError(t, err) {
t.FailNow()
}
//write resource file to temp dir
// write resource file to temp dir
resource, err := ioutil.TempFile("", "k8s-cli-*.yaml")
if !assert.NoError(t, err) {
t.FailNow()
@@ -62,9 +65,15 @@ func TestDeleterCreator_Delete(t *testing.T) {
t.FailNow()
}
//add a delete creator
// add a delete creator
dc := DeleterCreator{
Name: "image",
Name: "image",
DefinitionPrefix: fieldmeta.SetterDefinitionPrefix,
}
err = openapi.AddSchemaFromFile(openAPI.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
err = dc.Delete(openAPI.Name(), resource.Name())

View File

@@ -9,16 +9,20 @@ import (
"sigs.k8s.io/kustomize/kyaml/setters2"
)
// DeleterCreator delete a setter in the OpenAPI definitions, and removes references
// to the setter from matching resource fields.
// DeleterCreator delete a definition in the OpenAPI definitions, and removes references
// to the definition from matching resource fields.
type DeleterCreator struct {
// Name is the name of the setter to create or update.
// Name is the name of the setter or substitution to delete
Name string
// DefinitionPrefix is the prefix of the OpenAPI definition type
DefinitionPrefix string
}
func (d DeleterCreator) Delete(openAPIPath, resourcesPath string) error {
dd := setters2.DeleterDefinition{
Name: d.Name,
Name: d.Name,
DefinitionPrefix: d.DefinitionPrefix,
}
if err := dd.DeleteFromFile(openAPIPath); err != nil {
return err
@@ -35,7 +39,8 @@ func (d DeleterCreator) Delete(openAPIPath, resourcesPath string) error {
Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll(
&setters2.Delete{
FieldName: d.Name,
Name: d.Name,
DefinitionPrefix: d.DefinitionPrefix,
})},
Outputs: []kio.Writer{inout},
}.Execute()

View File

@@ -78,7 +78,8 @@ func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
Outputs: []kio.Writer{inout},
}.Execute()
if a.Count == 0 {
fmt.Printf("setter %s doesn't match any field in resources, but creating setter definition\n", c.Name)
fmt.Printf("setter %s doesn't match any field in resource configs, "+
"but creating setter definition\n", c.Name)
}
if err != nil {
return err

View File

@@ -123,18 +123,25 @@ func (c SubstitutionCreator) Create(openAPIPath, resourcesPath string) error {
return err
}
// Update the resources with the setter reference
a := &setters2.Add{
FieldName: c.FieldName,
FieldValue: c.FieldValue,
Ref: fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + c.Name,
}
// Update the resources with the substitution reference
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath}
return kio.Pipeline{
Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll(
&setters2.Add{
FieldName: c.FieldName,
FieldValue: c.FieldValue,
Ref: fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + c.Name,
})},
err = kio.Pipeline{
Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll(a)},
Outputs: []kio.Writer{inout},
}.Execute()
if a.Count == 0 {
fmt.Printf("substitution %s doesn't match any field value in resource configs, "+
"but creating substitution definition\n", c.Name)
}
return err
}
// createMarkersAndRefs takes the input pattern, creates setter/substitution markers

View File

@@ -110,7 +110,7 @@ func getSchema(r *yaml.RNode, s *openapi.ResourceSchema, field string) *openapi.
return s
}
if yaml.IsEmpty(r) {
if yaml.IsMissingOrNull(r) {
return nil
}

View File

@@ -70,7 +70,7 @@ func (e ElementSetter) Filter(rn *RNode) (*RNode, error) {
newNode := NewRNode(elem)
// empty elements are not valid -- they at least need an associative key
if IsEmpty(newNode) || IsEmptyMap(newNode) {
if IsMissingOrNull(newNode) || IsEmptyMap(newNode) {
continue
}
@@ -447,6 +447,9 @@ type FieldSetter struct {
// value on the ScalarNode.
Name string `yaml:"name,omitempty"`
// Comments for the field
Comments Comments `yaml:"comments,omitempty"`
// Value is the value to set.
// Optional if Kind is set.
Value *RNode `yaml:"value,omitempty"`
@@ -482,7 +485,7 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
}
// Clear the field if it is empty, or explicitly null
if s.Value == nil || IsNull(s.Value) {
if s.Value == nil || s.Value.IsTaggedNull() {
return rn.Pipe(Clear(s.Name))
}
@@ -504,7 +507,8 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
// create the field
rn.YNode().Content = append(rn.YNode().Content,
&yaml.Node{Kind: yaml.ScalarNode, Value: s.Name},
&yaml.Node{Kind: yaml.ScalarNode, HeadComment: s.Comments.HeadComment,
LineComment: s.Comments.LineComment, FootComment: s.Comments.FootComment, Value: s.Name},
s.Value.YNode())
return s.Value, nil
}
@@ -549,7 +553,7 @@ func IsFoundOrError(rn *RNode, err error) bool {
func ErrorIfAnyInvalidAndNonNull(kind yaml.Kind, rn ...*RNode) error {
for i := range rn {
if IsEmpty(rn[i]) {
if IsMissingOrNull(rn[i]) {
continue
}
if err := ErrorIfInvalid(rn[i], kind); err != nil {
@@ -568,7 +572,7 @@ var nodeTypeIndex = map[yaml.Kind]string{
}
func ErrorIfInvalid(rn *RNode, kind yaml.Kind) error {
if rn == nil || rn.YNode() == nil || IsNull(rn) {
if IsMissingOrNull(rn) {
// node has no type, pass validation
return nil
}

View File

@@ -698,11 +698,15 @@ metadata:
return
}
assert.Equal(t, ResourceMeta{
Kind: "Deployment",
APIVersion: "v1/apps",
TypeMeta: TypeMeta{
Kind: "Deployment",
APIVersion: "v1/apps",
},
ObjectMeta: ObjectMeta{
Name: "foo",
Namespace: "bar",
NameMeta: NameMeta{
Name: "foo",
Namespace: "bar",
},
Annotations: map[string]string{
"ka": "va",
},

40
kyaml/yaml/mapnode.go Normal file
View File

@@ -0,0 +1,40 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml
// MapNode wraps a field key and value.
type MapNode struct {
Key *RNode
Value *RNode
}
// IsNilOrEmpty returns true if the MapNode is nil,
// has no value, or has a value that appears empty.
func (mn *MapNode) IsNilOrEmpty() bool {
return mn == nil || mn.Value.IsNilOrEmpty()
}
type MapNodeSlice []*MapNode
func (m MapNodeSlice) Keys() []*RNode {
var keys []*RNode
for i := range m {
if m[i] != nil {
keys = append(keys, m[i].Key)
}
}
return keys
}
func (m MapNodeSlice) Values() []*RNode {
var values []*RNode
for i := range m {
if m[i] != nil {
values = append(values, m[i].Value)
} else {
values = append(values, nil)
}
}
return values
}

View File

@@ -0,0 +1,51 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml
import (
"testing"
)
func TestMapNodeIsNilOrEmpty(t *testing.T) {
var mn *MapNode
if !mn.IsNilOrEmpty() {
t.Fatalf("nil should be empty")
}
mn = &MapNode{Key: MakeNullNode()}
if !mn.IsNilOrEmpty() {
t.Fatalf("missing value should be empty")
}
mn.Value = NewRNode(nil)
if !mn.IsNilOrEmpty() {
t.Fatalf("missing value YNode should be empty")
}
mn.Value = MakeNullNode()
if !mn.IsNilOrEmpty() {
t.Fatalf("value tagged null should be empty")
}
mn.Value = NewMapRNode(nil)
if !mn.IsNilOrEmpty() {
t.Fatalf("empty map should be empty")
}
mn.Value = NewMapRNode(&map[string]string{"foo": "bar"})
if mn.IsNilOrEmpty() {
t.Fatalf("non-empty map should not be empty")
}
mn.Value = NewListRNode()
if !mn.IsNilOrEmpty() {
t.Fatalf("empty list should be empty")
}
mn.Value = NewListRNode("foo")
if mn.IsNilOrEmpty() {
t.Fatalf("non-empty list should not be empty")
}
}

View File

@@ -52,11 +52,11 @@ func (m Merger) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.R
if err := m.SetStyle(nodes); err != nil {
return nil, err
}
if yaml.IsEmpty(nodes.Dest()) {
if yaml.IsMissingOrNull(nodes.Dest()) {
// Add
return nodes.Origin(), nil
}
if yaml.IsNull(nodes.Origin()) {
if nodes.Origin().IsTaggedNull() {
// clear the value
return walk.ClearNode, nil
}
@@ -108,11 +108,11 @@ func (m Merger) VisitList(nodes walk.Sources, s *openapi.ResourceSchema, kind wa
}
// Add
if yaml.IsEmpty(nodes.Dest()) {
if yaml.IsMissingOrNull(nodes.Dest()) {
return nodes.Origin(), nil
}
// Clear
if yaml.IsNull(nodes.Origin()) {
if nodes.Origin().IsTaggedNull() {
return walk.ClearNode, nil
}
// Recursively Merge dest

View File

@@ -55,12 +55,108 @@ list: # new value
apiVersion: apps/v1`,
expected: `
apiVersion: apps/v1
list:
list: # new value
- 2
- 3
- 4
`},
//
// Test Case
//
{
description: `Update comment`,
origin: `
list: # comment
- 1
- 2
- 3`,
update: `
list: # updated comment
- 2
- 3
- 4`,
local: `
list: # comment
- 1
- 2
- 3`,
expected: `
list: # updated comment
- 2
- 3
- 4`},
//
// Test Case
//
{
description: `Don't update local modified comment`,
origin: `
list: # origin comment
- 1
- 2
- 3`,
update: `
list: # updated comment
- 2
- 3
- 4`,
local: `
list: # local comment
- 1
- 2
- 3`,
expected: `
list: # local comment
- 2
- 3
- 4`},
//
// Test Case
//
{
description: `Don't add local deleted comment`,
origin: `
list: # origin comment
- 1
- 2
- 3`,
update: `
list: # updated comment
- 2
- 3
- 4`,
local: `
list:
- 1
- 2
- 3`,
expected: `
list:
- 2
- 3
- 4`},
{
description: `Add update with comment`,
origin: `
apiVersion: apps/v1
`,
update: `
list: # updated comment
- 2
- 3
- 4`,
local: `
apiVersion: apps/v1`,
expected: `
list: # updated comment
- 2
- 3
- 4`},
//
// Test Case
//
@@ -140,14 +236,14 @@ list: # ignore value
- 3`,
local: `
apiVersion: apps/v1
list:
list: # local comment
- 2
- 3
- 4
`,
expected: `
apiVersion: apps/v1
list:
list: # local comment
- 2
- 3
- 4

View File

@@ -14,8 +14,9 @@ func Merge(dest, original, update *yaml.RNode) (*yaml.RNode, error) {
// if update == nil && original != nil => declarative deletion
return walk.Walker{
Visitor: Visitor{},
Sources: []*yaml.RNode{dest, original, update}}.Walk()
Visitor: Visitor{},
VisitKeysAsScalars: true,
Sources: []*yaml.RNode{dest, original, update}}.Walk()
}
func MergeStrings(dest, original, update string, infer bool) (string, error) {
@@ -35,6 +36,7 @@ func MergeStrings(dest, original, update string, infer bool) (string, error) {
result, err := walk.Walker{
InferAssociativeLists: infer,
Visitor: Visitor{},
VisitKeysAsScalars: true,
Sources: []*yaml.RNode{d, srcOriginal, srcUpdated}}.Walk()
if err != nil {
return "", err

View File

@@ -33,7 +33,7 @@ kind: StatefulSet # new value`},
// Test Case
//
{
description: `Ensure comments are updated`,
description: `Ensure comments are added`,
origin: `
apiVersion: apps/v1
kind: Deployment
@@ -81,6 +81,58 @@ spec:
replicas: 3 # {"$openapi":"replicas"}
`},
//
// Test Case
//
{
description: `Ensure comments are updated`,
origin: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/merge-source: 'dest'
config.kubernetes.io/path: 'temp.yaml'
spec:
replicas: 3 # {"$openapi":"replicas"}`,
update: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/merge-source: 'updated'
config.kubernetes.io/path: 'temp.yaml'
spec:
replicas: 3 # {"$openapi":"replicas_new"}`,
local: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/merge-source: 'original'
config.kubernetes.io/path: 'temp.yaml'
spec:
replicas: 3 # {"$openapi":"replicas"}
`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/merge-source: 'updated'
config.kubernetes.io/path: 'temp.yaml'
spec:
replicas: 3 # {"$openapi":"replicas_new"}
`},
{
description: `Ensure deleted comments are not updated`,
origin: `

View File

@@ -4,7 +4,6 @@
package merge3
import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/walk"
@@ -20,7 +19,7 @@ const (
type Visitor struct{}
func (m Visitor) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
// explicitly cleared from either dest or update
return walk.ClearNode, nil
}
@@ -34,16 +33,17 @@ func (m Visitor) VisitMap(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.
// initialize a new value that can be recursively merged
return yaml.NewRNode(&yaml.Node{Kind: yaml.MappingNode}), nil
}
// recursively merge the dest with the original and updated
return nodes.Dest(), nil
}
func (m Visitor) visitAList(nodes walk.Sources, _ *openapi.ResourceSchema) (*yaml.RNode, error) {
if yaml.IsEmpty(nodes.Updated()) && !yaml.IsEmpty(nodes.Origin()) {
if yaml.IsMissingOrNull(nodes.Updated()) && !yaml.IsMissingOrNull(nodes.Origin()) {
// implicitly cleared from update -- element was deleted
return walk.ClearNode, nil
}
if yaml.IsEmpty(nodes.Dest()) {
if yaml.IsMissingOrNull(nodes.Dest()) {
// not cleared, but missing from the dest
// initialize a new value that can be recursively merged
return yaml.NewRNode(&yaml.Node{Kind: yaml.SequenceNode}), nil
@@ -54,30 +54,26 @@ func (m Visitor) visitAList(nodes walk.Sources, _ *openapi.ResourceSchema) (*yam
}
func (m Visitor) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*yaml.RNode, error) {
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
// explicitly cleared from either dest or update
return nil, nil
}
if yaml.IsEmpty(nodes.Updated()) != yaml.IsEmpty(nodes.Origin()) {
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
// value added or removed in update
return nodes.Updated(), nil
}
if yaml.IsEmpty(nodes.Updated()) && yaml.IsEmpty(nodes.Origin()) {
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
// value added or removed in update
return nodes.Dest(), nil
}
updatedStr, err := nodes.Updated().String()
values, err := m.getStrValues(nodes)
if err != nil {
return nil, errors.Wrap(err)
}
originStr, err := nodes.Origin().String()
if err != nil {
return nil, errors.Wrap(err)
return nil, err
}
if updatedStr != originStr {
// change in update node
if (values.Dest == "" || values.Dest == values.Origin) && values.Origin != values.Update {
// if local is nil or is unchanged but there is new update
return nodes.Updated(), nil
}
@@ -91,16 +87,16 @@ func (m Visitor) VisitScalar(nodes walk.Sources, s *openapi.ResourceSchema) (*ya
}
func (m Visitor) visitNAList(nodes walk.Sources) (*yaml.RNode, error) {
if yaml.IsNull(nodes.Updated()) || yaml.IsNull(nodes.Dest()) {
if nodes.Updated().IsTaggedNull() || nodes.Dest().IsTaggedNull() {
// explicitly cleared from either dest or update
return walk.ClearNode, nil
}
if yaml.IsEmpty(nodes.Updated()) != yaml.IsEmpty(nodes.Origin()) {
if yaml.IsMissingOrNull(nodes.Updated()) != yaml.IsMissingOrNull(nodes.Origin()) {
// value added or removed in update
return nodes.Updated(), nil
}
if yaml.IsEmpty(nodes.Updated()) && yaml.IsEmpty(nodes.Origin()) {
if yaml.IsMissingOrNull(nodes.Updated()) && yaml.IsMissingOrNull(nodes.Origin()) {
// value not present in source or dest
return nodes.Dest(), nil
}

600
kyaml/yaml/rnode.go Normal file
View File

@@ -0,0 +1,600 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml
import (
"encoding/json"
"fmt"
"io/ioutil"
"gopkg.in/yaml.v3"
"sigs.k8s.io/kustomize/kyaml/errors"
)
const (
// NodeTagNull is the tag set for a yaml.Document that contains no data;
// e.g. it isn't a Map, Slice, Document, etc
NodeTagNull = "!!null"
NodeTagFloat = "!!float"
NodeTagString = "!!str"
NodeTagBool = "!!bool"
NodeTagInt = "!!int"
NodeTagMap = "!!map"
NodeTagSeq = "!!seq"
NodeTagEmpty = ""
)
// MakeNullNode returns an RNode that represents an empty document.
func MakeNullNode() *RNode {
return NewRNode(&Node{Tag: NodeTagNull})
}
// IsMissingOrNull is true if the RNode is nil or explicitly tagged null.
// TODO: make this a method on RNode.
func IsMissingOrNull(node *RNode) bool {
return node.IsNil() || node.YNode().Tag == NodeTagNull
}
// IsEmptyMap returns true if the RNode is an empty node or an empty map.
// TODO: make this a method on RNode.
func IsEmptyMap(node *RNode) bool {
return IsMissingOrNull(node) || IsYNodeEmptyMap(node.YNode())
}
// GetValue returns underlying yaml.Node Value field
func GetValue(node *RNode) string {
if IsMissingOrNull(node) {
return ""
}
return node.YNode().Value
}
// Parse parses a yaml string into an *RNode
func Parse(value string) (*RNode, error) {
return Parser{Value: value}.Filter(nil)
}
// ReadFile parses a single Resource from a yaml file
func ReadFile(path string) (*RNode, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
return Parse(string(b))
}
// WriteFile writes a single Resource to a yaml file
func WriteFile(node *RNode, path string) error {
out, err := node.String()
if err != nil {
return err
}
return ioutil.WriteFile(path, []byte(out), 0600)
}
// UpdateFile reads the file at path, applies the filter to it, and write the result back.
// path must contain a exactly 1 resource (YAML).
func UpdateFile(filter Filter, path string) error {
// Read the yaml
y, err := ReadFile(path)
if err != nil {
return err
}
// Update the yaml
if err := y.PipeE(filter); err != nil {
return err
}
// Write the yaml
return WriteFile(y, path)
}
// MustParse parses a yaml string into an *RNode and panics if there is an error
func MustParse(value string) *RNode {
v, err := Parser{Value: value}.Filter(nil)
if err != nil {
panic(err)
}
return v
}
// NewScalarRNode returns a new Scalar *RNode containing the provided scalar value.
func NewScalarRNode(value string) *RNode {
return &RNode{
value: &yaml.Node{
Kind: yaml.ScalarNode,
Value: value,
}}
}
// NewListRNode returns a new List *RNode containing the provided scalar values.
func NewListRNode(values ...string) *RNode {
seq := &RNode{value: &yaml.Node{Kind: yaml.SequenceNode}}
for _, v := range values {
seq.value.Content = append(seq.value.Content, &yaml.Node{
Kind: yaml.ScalarNode,
Value: v,
})
}
return seq
}
// NewMapRNode returns a new Map *RNode containing the provided values
func NewMapRNode(values *map[string]string) *RNode {
m := &RNode{value: &yaml.Node{
Kind: yaml.MappingNode,
}}
if values == nil {
return m
}
for k, v := range *values {
m.value.Content = append(m.value.Content, &yaml.Node{
Kind: yaml.ScalarNode,
Value: k,
}, &yaml.Node{
Kind: yaml.ScalarNode,
Value: v,
})
}
return m
}
// NewRNode returns a new RNode pointer containing the provided Node.
func NewRNode(value *yaml.Node) *RNode {
return &RNode{value: value}
}
// RNode provides functions for manipulating Kubernetes Resources
// Objects unmarshalled into *yaml.Nodes
type RNode struct {
// fieldPath contains the path from the root of the KubernetesObject to
// this field.
// Only field names are captured in the path.
// e.g. a image field in a Deployment would be
// 'spec.template.spec.containers.image'
fieldPath []string
// FieldValue contains the value.
// FieldValue is always set:
// field: field value
// list entry: list entry value
// object root: object root
value *yaml.Node
Match []string
}
// Copy returns a distinct copy.
func (rn *RNode) Copy() *RNode {
if rn == nil {
return nil
}
result := *rn
result.value = CopyYNode(rn.value)
return &result
}
var ErrMissingMetadata = fmt.Errorf("missing Resource metadata")
// Field names
const (
AnnotationsField = "annotations"
APIVersionField = "apiVersion"
KindField = "kind"
MetadataField = "metadata"
NameField = "name"
NamespaceField = "namespace"
LabelsField = "labels"
)
// IsNil is true if the node is nil, or its underlying YNode is nil.
func (rn *RNode) IsNil() bool {
return rn == nil || rn.YNode() == nil
}
// IsTaggedNull is true if a non-nil node is explicitly tagged Null.
func (rn *RNode) IsTaggedNull() bool {
return !rn.IsNil() && IsYNodeTaggedNull(rn.YNode())
}
// IsNilOrEmpty is true if the node is nil,
// has no YNode, or has YNode that appears empty.
func (rn *RNode) IsNilOrEmpty() bool {
return rn.IsNil() ||
IsYNodeTaggedNull(rn.YNode()) ||
IsYNodeEmptyMap(rn.YNode()) ||
IsYNodeEmptySeq(rn.YNode())
}
// GetMeta returns the ResourceMeta for an RNode
func (rn *RNode) GetMeta() (ResourceMeta, error) {
if IsMissingOrNull(rn) {
return ResourceMeta{}, nil
}
missingMeta := true
n := rn
if n.YNode().Kind == DocumentNode {
// get the content is this is the document node
n = NewRNode(n.Content()[0])
}
// don't decode into the struct directly or it will fail on UTF-8 issues
// which appear in comments
m := ResourceMeta{}
// TODO: consider optimizing this parsing
if f := n.Field(APIVersionField); !f.IsNilOrEmpty() {
m.APIVersion = GetValue(f.Value)
missingMeta = false
}
if f := n.Field(KindField); !f.IsNilOrEmpty() {
m.Kind = GetValue(f.Value)
missingMeta = false
}
mf := n.Field(MetadataField)
if mf.IsNilOrEmpty() {
if missingMeta {
return m, ErrMissingMetadata
}
return m, nil
}
meta := mf.Value
if f := meta.Field(NameField); !f.IsNilOrEmpty() {
m.Name = f.Value.YNode().Value
missingMeta = false
}
if f := meta.Field(NamespaceField); !f.IsNilOrEmpty() {
m.Namespace = GetValue(f.Value)
missingMeta = false
}
if f := meta.Field(LabelsField); !f.IsNilOrEmpty() {
m.Labels = map[string]string{}
_ = f.Value.VisitFields(func(node *MapNode) error {
m.Labels[GetValue(node.Key)] = GetValue(node.Value)
return nil
})
missingMeta = false
}
if f := meta.Field(AnnotationsField); !f.IsNilOrEmpty() {
m.Annotations = map[string]string{}
_ = f.Value.VisitFields(func(node *MapNode) error {
m.Annotations[GetValue(node.Key)] = GetValue(node.Value)
return nil
})
missingMeta = false
}
if missingMeta {
return m, ErrMissingMetadata
}
return m, nil
}
// Pipe sequentially invokes each GrepFilter, and passes the result to the next
// GrepFilter.
//
// Analogous to http://www.linfo.org/pipes.html
//
// * rn is provided as input to the first GrepFilter.
// * if any GrepFilter returns an error, immediately return the error
// * if any GrepFilter returns a nil RNode, immediately return nil, nil
// * if all Filters succeed with non-empty results, return the final result
func (rn *RNode) Pipe(functions ...Filter) (*RNode, error) {
// check if rn is nil to make chaining Pipe calls easier
if rn == nil {
return nil, nil
}
var v *RNode
var err error
if rn.value != nil && rn.value.Kind == yaml.DocumentNode {
// the first node may be a DocumentNode containing a single MappingNode
v = &RNode{value: rn.value.Content[0]}
} else {
v = rn
}
// return each fn in sequence until encountering an error or missing value
for _, c := range functions {
v, err = c.Filter(v)
if err != nil || v == nil {
return v, errors.Wrap(err)
}
}
return v, err
}
// PipeE runs Pipe, dropping the *RNode return value.
// Useful for directly returning the Pipe error value from functions.
func (rn *RNode) PipeE(functions ...Filter) error {
_, err := rn.Pipe(functions...)
return errors.Wrap(err)
}
// Document returns the Node RNode for the value. Does not unwrap the node if it is a
// DocumentNodes
func (rn *RNode) Document() *yaml.Node {
return rn.value
}
// YNode returns the yaml.Node value. If the yaml.Node value is a DocumentNode,
// YNode will return the DocumentNode Content entry instead of the DocumentNode.
func (rn *RNode) YNode() *yaml.Node {
if rn == nil || rn.value == nil {
return nil
}
if rn.value.Kind == yaml.DocumentNode {
return rn.value.Content[0]
}
return rn.value
}
// SetYNode sets the yaml.Node value on an RNode.
func (rn *RNode) SetYNode(node *yaml.Node) {
if rn.value == nil || node == nil {
rn.value = node
return
}
*rn.value = *node
}
// AppendToFieldPath appends a field name to the FieldPath.
func (rn *RNode) AppendToFieldPath(parts ...string) {
rn.fieldPath = append(rn.fieldPath, parts...)
}
// FieldPath returns the field path from the Resource root node, to rn.
// Does not include list indexes.
func (rn *RNode) FieldPath() []string {
return rn.fieldPath
}
// String returns string representation of the RNode
func (rn *RNode) String() (string, error) {
if rn == nil {
return "", nil
}
return String(rn.value)
}
// MustString returns string representation of the RNode or panics if there is an error
func (rn *RNode) MustString() string {
s, err := rn.String()
if err != nil {
panic(err)
}
return s
}
// Content returns Node Content field.
func (rn *RNode) Content() []*yaml.Node {
if rn == nil {
return nil
}
return rn.YNode().Content
}
// Fields returns the list of field names for a MappingNode.
// Returns an error for non-MappingNodes.
func (rn *RNode) Fields() ([]string, error) {
if err := ErrorIfInvalid(rn, yaml.MappingNode); err != nil {
return nil, errors.Wrap(err)
}
var fields []string
for i := 0; i < len(rn.Content()); i += 2 {
fields = append(fields, rn.Content()[i].Value)
}
return fields, nil
}
// FieldRNodes returns the list of field key RNodes for a MappingNode.
// Returns an error for non-MappingNodes.
func (rn *RNode) FieldRNodes() ([]*RNode, error) {
if err := ErrorIfInvalid(rn, yaml.MappingNode); err != nil {
return nil, errors.Wrap(err)
}
var fields []*RNode
for i := 0; i < len(rn.Content()); i += 2 {
yNode := rn.Content()[i]
// for each key node in the input mapping node contents create equivalent rNode
rNode := &RNode{}
rNode.SetYNode(yNode)
fields = append(fields, rNode)
}
return fields, nil
}
// Field returns a fieldName, fieldValue pair for MappingNodes.
// Returns nil for non-MappingNodes.
func (rn *RNode) Field(field string) *MapNode {
if rn.YNode().Kind != yaml.MappingNode {
return nil
}
for i := 0; i < len(rn.Content()); i = IncrementFieldIndex(i) {
isMatchingField := rn.Content()[i].Value == field
if isMatchingField {
return &MapNode{Key: NewRNode(rn.Content()[i]), Value: NewRNode(rn.Content()[i+1])}
}
}
return nil
}
// VisitFields calls fn for each field in the RNode.
// Returns an error for non-MappingNodes.
func (rn *RNode) VisitFields(fn func(node *MapNode) error) error {
// get the list of srcFieldNames
srcFieldNames, err := rn.Fields()
if err != nil {
return errors.Wrap(err)
}
// visit each field
for _, fieldName := range srcFieldNames {
if err := fn(rn.Field(fieldName)); err != nil {
return errors.Wrap(err)
}
}
return nil
}
// Elements returns the list of elements in the RNode.
// Returns an error for non-SequenceNodes.
func (rn *RNode) Elements() ([]*RNode, error) {
if err := ErrorIfInvalid(rn, yaml.SequenceNode); err != nil {
return nil, errors.Wrap(err)
}
var elements []*RNode
for i := 0; i < len(rn.Content()); i++ {
elements = append(elements, NewRNode(rn.Content()[i]))
}
return elements, nil
}
// ElementValues returns a list of all observed values for a given field name in a
// list of elements.
// Returns error for non-SequenceNodes.
func (rn *RNode) ElementValues(key string) ([]string, error) {
if err := ErrorIfInvalid(rn, yaml.SequenceNode); err != nil {
return nil, errors.Wrap(err)
}
var elements []string
for i := 0; i < len(rn.Content()); i++ {
field := NewRNode(rn.Content()[i]).Field(key)
if !field.IsNilOrEmpty() {
elements = append(elements, field.Value.YNode().Value)
}
}
return elements, nil
}
// Element returns the element in the list which contains the field matching the value.
// Returns nil for non-SequenceNodes or if no Element matches.
func (rn *RNode) Element(key, value string) *RNode {
if rn.YNode().Kind != yaml.SequenceNode {
return nil
}
elem, err := rn.Pipe(MatchElement(key, value))
if err != nil {
return nil
}
return elem
}
// VisitElements calls fn for each element in a SequenceNode.
// Returns an error for non-SequenceNodes
func (rn *RNode) VisitElements(fn func(node *RNode) error) error {
elements, err := rn.Elements()
if err != nil {
return errors.Wrap(err)
}
for i := range elements {
if err := fn(elements[i]); err != nil {
return errors.Wrap(err)
}
}
return nil
}
// AssociativeSequenceKeys is a map of paths to sequences that have associative keys.
// The order sets the precedence of the merge keys -- if multiple keys are present
// in Resources in a list, then the FIRST key which ALL elements in the list have is used as the
// associative key for merging that list.
// Only infer name as a merge key.
var AssociativeSequenceKeys = []string{"name"}
// IsAssociative returns true if the RNode contains an AssociativeSequenceKey as a field.
func (rn *RNode) IsAssociative() bool {
return rn.GetAssociativeKey() != ""
}
// GetAssociativeKey returns the AssociativeSequenceKey used to merge the elements in the
// SequenceNode, or "" if the list is not associative.
func (rn *RNode) GetAssociativeKey() string {
// look for any associative keys in the first element
for _, key := range AssociativeSequenceKeys {
if checkKey(key, rn.Content()) {
return key
}
}
// element doesn't have an associative keys
return ""
}
func (rn *RNode) MarshalJSON() ([]byte, error) {
s, err := rn.String()
if err != nil {
return nil, err
}
if rn.YNode().Kind == SequenceNode {
var a []interface{}
if err := Unmarshal([]byte(s), &a); err != nil {
return nil, err
}
return json.Marshal(a)
}
m := map[string]interface{}{}
if err := Unmarshal([]byte(s), &m); err != nil {
return nil, err
}
return json.Marshal(m)
}
func (rn *RNode) UnmarshalJSON(b []byte) error {
m := map[string]interface{}{}
if err := json.Unmarshal(b, &m); err != nil {
return err
}
c, err := Marshal(m)
if err != nil {
return err
}
r, err := Parse(string(c))
if err != nil {
return err
}
rn.value = r.value
return nil
}
// ConvertJSONToYamlNode parses input json string and returns equivalent yaml node
func ConvertJSONToYamlNode(jsonStr string) (*RNode, error) {
var body map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &body)
if err != nil {
return nil, err
}
yml, err := yaml.Marshal(body)
if err != nil {
return nil, err
}
node, err := Parse(string(yml))
if err != nil {
return nil, err
}
return node, nil
}
// checkKey returns true if all elems have the key
func checkKey(key string, elems []*Node) bool {
count := 0
for i := range elems {
elem := NewRNode(elems[i])
if elem.Field(key) != nil {
count++
}
}
return count == len(elems)
}

391
kyaml/yaml/rnode_test.go Normal file
View File

@@ -0,0 +1,391 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml
import (
"reflect"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
// Test that non-UTF8 characters in comments don't cause failures
func TestRNode_GetMeta_UTF16(t *testing.T) {
sr, err := Parse(`apiVersion: rbac.istio.io/v1alpha1
kind: ServiceRole
metadata:
name: wildcard
namespace: default
# If set to [“*”], it refers to all services in the namespace
annotations:
foo: bar
spec:
rules:
# There is one service in default namespace, should not result in a validation error
# If set to [“*”], it refers to all services in the namespace
- services: ["*"]
methods: ["GET", "HEAD"]
`)
if !assert.NoError(t, err) {
t.FailNow()
}
actual, err := sr.GetMeta()
if !assert.NoError(t, err) {
t.FailNow()
}
expected := ResourceMeta{
TypeMeta: TypeMeta{
APIVersion: "rbac.istio.io/v1alpha1",
Kind: "ServiceRole",
},
ObjectMeta: ObjectMeta{
NameMeta: NameMeta{
Name: "wildcard",
Namespace: "default",
},
Annotations: map[string]string{"foo": "bar"},
},
}
if !assert.Equal(t, expected, actual) {
t.FailNow()
}
}
func TestRNode_UnmarshalJSON(t *testing.T) {
testCases := []struct {
testName string
input string
output string
}{
{
testName: "simple document",
input: `{"hello":"world"}`,
output: `hello: world`,
},
{
testName: "nested structure",
input: `
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "my-deployment",
"namespace": "default"
}
}
`,
output: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: default
`,
},
}
for i := range testCases {
tc := testCases[i]
t.Run(tc.testName, func(t *testing.T) {
instance := &RNode{}
err := instance.UnmarshalJSON([]byte(tc.input))
if !assert.NoError(t, err) {
t.FailNow()
}
actual, err := instance.String()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(tc.output), strings.TrimSpace(actual)) {
t.FailNow()
}
})
}
}
func TestRNode_MarshalJSON(t *testing.T) {
tests := []struct {
name string
ydoc string
want string
}{
{
name: "object",
ydoc: `
hello: world
`,
want: `{"hello":"world"}`,
},
{
name: "array",
ydoc: `
- name: s1
- name: s2
`,
want: `[{"name":"s1"},{"name":"s2"}]`,
},
}
for idx := range tests {
tt := tests[idx]
t.Run(tt.name, func(t *testing.T) {
instance, err := Parse(tt.ydoc)
if !assert.NoError(t, err) {
t.FailNow()
}
actual, err := instance.MarshalJSON()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(tt.want), strings.TrimSpace(string(actual))) {
t.FailNow()
}
})
}
}
func TestConvertJSONToYamlNode(t *testing.T) {
inputJSON := `{"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`
expected := `enum:
- allowedValue1
- allowedValue2
maxLength: 15
type: string
`
node, err := ConvertJSONToYamlNode(inputJSON)
if !assert.NoError(t, err) {
t.FailNow()
}
actual, err := node.String()
if !assert.NoError(t, err) {
t.FailNow()
}
assert.Equal(t, expected, actual)
}
func TestIsMissingOrNull(t *testing.T) {
if !IsMissingOrNull(nil) {
t.Fatalf("input: nil")
}
// missing value or null value
if !IsMissingOrNull(NewRNode(nil)) {
t.Fatalf("input: nil value")
}
if IsMissingOrNull(NewScalarRNode("foo")) {
t.Fatalf("input: valid node")
}
// node with NullNodeTag
if !IsMissingOrNull(MakeNullNode()) {
t.Fatalf("input: with NullNodeTag")
}
// empty array. empty array is not expected as empty
if IsMissingOrNull(NewListRNode()) {
t.Fatalf("input: empty array")
}
// array with 1 item
node := NewListRNode("foo")
if IsMissingOrNull(node) {
t.Fatalf("input: array with 1 item")
}
// delete the item in array
node.value.Content = nil
if IsMissingOrNull(node) {
t.Fatalf("input: empty array")
}
}
func TestCopy(t *testing.T) {
rn := RNode{
fieldPath: []string{"fp1", "fp2"},
value: &Node{
Kind: 200,
},
Match: []string{"m1", "m2"},
}
rnC := rn.Copy()
if !reflect.DeepEqual(&rn, rnC) {
t.Fatalf("copy %v is not deep equal to %v", rnC, rn)
}
tmp := rn.value.Kind
rn.value.Kind = 666
if reflect.DeepEqual(rn, rnC) {
t.Fatalf("changing component should break equality")
}
rn.value.Kind = tmp
if !reflect.DeepEqual(&rn, rnC) {
t.Fatalf("should be back to normal")
}
rn.fieldPath[0] = "Different"
if reflect.DeepEqual(rn, rnC) {
t.Fatalf("changing fieldpath should break equality")
}
}
func TestFieldRNodes(t *testing.T) {
testCases := []struct {
testName string
input string
output []string
err string
}{
{
testName: "simple document",
input: `apiVersion: example.com/v1beta1
kind: Example1
spec:
list:
- "a"
- "b"
- "c"`,
output: []string{"apiVersion", "kind", "spec", "list"},
},
{
testName: "non mapping node error",
input: `apiVersion`,
err: "wrong Node Kind for expected: MappingNode was ScalarNode: value: {apiVersion}",
},
}
for i := range testCases {
tc := testCases[i]
t.Run(tc.testName, func(t *testing.T) {
rNode, err := Parse(tc.input)
if !assert.NoError(t, err) {
t.FailNow()
}
fieldRNodes, err := rNode.FieldRNodes()
if tc.err == "" {
if !assert.NoError(t, err) {
t.FailNow()
}
} else {
if !assert.Equal(t, tc.err, err.Error()) {
t.FailNow()
}
}
for i := range fieldRNodes {
actual, err := fieldRNodes[i].String()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, tc.output[i], strings.TrimSpace(actual)) {
t.FailNow()
}
}
})
}
}
func TestIsEmptyMap(t *testing.T) {
node := NewMapRNode(nil)
// empty map
if !IsEmptyMap(node) {
t.Fatalf("input: empty map")
}
// map with 1 item
node = NewMapRNode(&map[string]string{
"foo": "bar",
})
if IsEmptyMap(node) {
t.Fatalf("input: map with 1 item")
}
// delete the item in map
node.value.Content = nil
if !IsEmptyMap(node) {
t.Fatalf("input: empty map")
}
}
func TestIsNil(t *testing.T) {
var rn *RNode
if !rn.IsNil() {
t.Fatalf("uninitialized RNode should be nil")
}
if !NewRNode(nil).IsNil() {
t.Fatalf("missing value YNode should be nil")
}
if MakeNullNode().IsNil() {
t.Fatalf("value tagged null is not nil")
}
if NewMapRNode(nil).IsNil() {
t.Fatalf("empty map not nil")
}
if NewListRNode().IsNil() {
t.Fatalf("empty list not nil")
}
}
func TestIsTaggedNull(t *testing.T) {
var rn *RNode
if rn.IsTaggedNull() {
t.Fatalf("nil RNode cannot be tagged")
}
if NewRNode(nil).IsTaggedNull() {
t.Fatalf("bare RNode should not be tagged")
}
if !MakeNullNode().IsTaggedNull() {
t.Fatalf("a null node is tagged null by definition")
}
if NewMapRNode(nil).IsTaggedNull() {
t.Fatalf("empty map should not be tagged null")
}
if NewListRNode().IsTaggedNull() {
t.Fatalf("empty list should not be tagged null")
}
}
func TestRNodeIsNilOrEmpty(t *testing.T) {
var rn *RNode
if !rn.IsNilOrEmpty() {
t.Fatalf("uninitialized RNode should be empty")
}
if !NewRNode(nil).IsNilOrEmpty() {
t.Fatalf("missing value YNode should be empty")
}
if !MakeNullNode().IsNilOrEmpty() {
t.Fatalf("value tagged null should be empty")
}
if !NewMapRNode(nil).IsNilOrEmpty() {
t.Fatalf("empty map should be empty")
}
if NewMapRNode(&map[string]string{"foo": "bar"}).IsNilOrEmpty() {
t.Fatalf("non-empty map should not be empty")
}
if !NewListRNode().IsNilOrEmpty() {
t.Fatalf("empty list should be empty")
}
if NewListRNode("foo").IsNilOrEmpty() {
t.Fatalf("non-empty list should not be empty")
}
}

View File

@@ -22,7 +22,7 @@ func IsAssociative(schema *openapi.ResourceSchema, nodes []*yaml.RNode, infer bo
}
for i := range nodes {
node := nodes[i]
if yaml.IsEmpty(node) {
if yaml.IsMissingOrNull(node) {
continue
}
if node.IsAssociative() {

View File

@@ -5,9 +5,6 @@ package yaml
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"gopkg.in/yaml.v3"
@@ -15,66 +12,36 @@ import (
"sigs.k8s.io/kustomize/kyaml/sets"
)
const (
// NodeTagNull is the tag set for a yaml.Document that contains no data;
// e.g. it isn't a Map, Slice, Document, etc
NodeTagNull = "!!null"
NodeTagFloat = "!!float"
NodeTagString = "!!str"
NodeTagBool = "!!bool"
NodeTagInt = "!!int"
NodeTagMap = "!!map"
NodeTagSeq = "!!seq"
NodeTagEmpty = ""
)
// MakeNullNode returns an RNode that represents an empty document.
func MakeNullNode() *RNode {
return NewRNode(&Node{Tag: NodeTagNull})
}
// IsMissingOrNull returns true if the RNode is nil or contains and explicitly null value.
func IsMissingOrNull(node *RNode) bool {
return IsNil(node) || node.YNode().Tag == NodeTagNull
}
// IsEmpty returns true if the RNode is MissingOrNull
func IsEmpty(node *RNode) bool {
return IsMissingOrNull(node)
}
// IsEmptyMap returns true if the RNode is an empty node or an empty map
func IsEmptyMap(node *RNode) bool {
if IsEmpty(node) {
return true
// CopyYNode returns a distinct copy of its argument.
// Use https://github.com/jinzhu/copier instead?
func CopyYNode(n *yaml.Node) *yaml.Node {
if n == nil {
return nil
}
return IsYNodeEmptyMap(node.YNode())
}
// IsNil return true if the node is nil, or its underlying YNode is nil.
func IsNil(node *RNode) bool {
return node == nil || node.YNode() == nil
}
func IsNull(node *RNode) bool {
return !IsNil(node) && node.YNode().Tag == NodeTagNull
}
func IsFieldEmpty(node *MapNode) bool {
if node == nil || node.Value == nil || node.Value.YNode() == nil ||
node.Value.YNode().Tag == NodeTagNull {
return true
c := *n
if len(n.Content) > 0 {
// Using Go 'copy' here doesn't yield independent slices.
c.Content = make([]*Node, len(n.Content))
for i, item := range n.Content {
c.Content[i] = CopyYNode(item)
}
}
return IsYNodeEmptyMap(node.Value.YNode()) ||
IsYNodeEmptySeq(node.Value.YNode())
return &c
}
// IsYNodeTaggedNull returns true if the node is explicitly tagged Null.
func IsYNodeTaggedNull(n *yaml.Node) bool {
return n != nil && n.Tag == NodeTagNull
}
// IsYNodeEmptyMap is true if the Node is a non-nil empty map.
func IsYNodeEmptyMap(n *yaml.Node) bool {
return n.Kind == yaml.MappingNode && len(n.Content) == 0
return n != nil && n.Kind == yaml.MappingNode && len(n.Content) == 0
}
// IsYNodeEmptyMap is true if the Node is a non-nil empty sequence.
func IsYNodeEmptySeq(n *yaml.Node) bool {
return n.Kind == yaml.SequenceNode && len(n.Content) == 0
return n != nil && n.Kind == yaml.SequenceNode && len(n.Content) == 0
}
// IsYNodeEmptyDoc is true if the node is a Document with no content.
@@ -87,19 +54,6 @@ func IsYNodeString(n *yaml.Node) bool {
return n.Kind == yaml.ScalarNode && n.Tag == NodeTagString
}
// GetValue returns underlying yaml.Node Value field
func GetValue(node *RNode) string {
if IsEmpty(node) {
return ""
}
return node.YNode().Value
}
func IsFieldNull(node *MapNode) bool {
return node != nil && node.Value != nil && node.Value.YNode() != nil &&
node.Value.YNode().Tag == NodeTagNull
}
// Parser parses values into configuration.
type Parser struct {
Kind string `yaml:"kind,omitempty"`
@@ -112,47 +66,6 @@ func (p Parser) Filter(_ *RNode) (*RNode, error) {
return o, d.Decode(o.value)
}
// Parse parses a yaml string into an *RNode
func Parse(value string) (*RNode, error) {
return Parser{Value: value}.Filter(nil)
}
// ReadFile parses a single Resource from a yaml file
func ReadFile(path string) (*RNode, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
return Parse(string(b))
}
// WriteFile writes a single Resource to a yaml file
func WriteFile(node *RNode, path string) error {
out, err := node.String()
if err != nil {
return err
}
return ioutil.WriteFile(path, []byte(out), 0600)
}
// UpdateFile reads the file at path, applies the filter to it, and write the result back.
// path must contain a exactly 1 resource (YAML).
func UpdateFile(filter Filter, path string) error {
// Read the yaml
y, err := ReadFile(path)
if err != nil {
return err
}
// Update the yaml
if err := y.PipeE(filter); err != nil {
return err
}
// Write the yaml
return WriteFile(y, path)
}
// TODO(pwittrock): test this
func GetStyle(styles ...string) Style {
var style Style
@@ -175,63 +88,6 @@ func GetStyle(styles ...string) Style {
return style
}
// MustParse parses a yaml string into an *RNode and panics if there is an error
func MustParse(value string) *RNode {
v, err := Parser{Value: value}.Filter(nil)
if err != nil {
panic(err)
}
return v
}
// NewScalarRNode returns a new Scalar *RNode containing the provided scalar value.
func NewScalarRNode(value string) *RNode {
return &RNode{
value: &yaml.Node{
Kind: yaml.ScalarNode,
Value: value,
}}
}
// NewListRNode returns a new List *RNode containing the provided scalar values.
func NewListRNode(values ...string) *RNode {
seq := &RNode{value: &yaml.Node{Kind: yaml.SequenceNode}}
for _, v := range values {
seq.value.Content = append(seq.value.Content, &yaml.Node{
Kind: yaml.ScalarNode,
Value: v,
})
}
return seq
}
// NewMapRNode returns a new Map *RNode containing the provided values
func NewMapRNode(values *map[string]string) *RNode {
m := &RNode{value: &yaml.Node{
Kind: yaml.MappingNode,
}}
if values == nil {
return m
}
for k, v := range *values {
m.value.Content = append(m.value.Content, &yaml.Node{
Kind: yaml.ScalarNode,
Value: k,
}, &yaml.Node{
Kind: yaml.ScalarNode,
Value: v,
})
}
return m
}
// NewRNode returns a new RNode pointer containing the provided Node.
func NewRNode(value *yaml.Node) *RNode {
return &RNode{value: value}
}
// Filter defines a function to manipulate an individual RNode such as by changing
// its values, or returning a field.
//
@@ -249,77 +105,73 @@ func (f FilterFunc) Filter(object *RNode) (*RNode, error) {
return f(object)
}
// RNode provides functions for manipulating Kubernetes Resources
// Objects unmarshalled into *yaml.Nodes
type RNode struct {
// fieldPath contains the path from the root of the KubernetesObject to
// this field.
// Only field names are captured in the path.
// e.g. a image field in a Deployment would be
// 'spec.template.spec.containers.image'
fieldPath []string
// FieldValue contains the value.
// FieldValue is always set:
// field: field value
// list entry: list entry value
// object root: object root
value *yaml.Node
Match []string
}
// MapNode wraps a field key and value.
type MapNode struct {
Key *RNode
Value *RNode
}
type MapNodeSlice []*MapNode
func (m MapNodeSlice) Keys() []*RNode {
var keys []*RNode
for i := range m {
if m[i] != nil {
keys = append(keys, m[i].Key)
}
}
return keys
}
func (m MapNodeSlice) Values() []*RNode {
var values []*RNode
for i := range m {
if m[i] != nil {
values = append(values, m[i].Value)
} else {
values = append(values, nil)
}
}
return values
}
// TypeMeta partially copies apimachinery/pkg/apis/meta/v1.TypeMeta
// No need for a direct dependence; the fields are stable.
type TypeMeta struct {
Kind string
APIVersion string
// APIVersion is the apiVersion field of a Resource
APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"`
// Kind is the kind field of a Resource
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
}
// Hardcoded list.
// TODO(#2861): replace this with data acquired from openapi.
var notNamespaceableKinds = []string{
"APIService",
"CSIDriver",
"CSINode",
"CertificateSigningRequest",
"Cluster",
"ClusterRole",
"ClusterRoleBinding",
"ComponentStatus",
"CustomResourceDefinition",
"MutatingWebhookConfiguration",
"Namespace",
"Node",
"PersistentVolume",
"PodSecurityPolicy",
"PriorityClass",
"RuntimeClass",
"SelfSubjectAccessReview",
"SelfSubjectRulesReview",
"StorageClass",
"SubjectAccessReview",
"TokenReview",
"ValidatingWebhookConfiguration",
"VolumeAttachment",
}
// IsNamespaceable returns true if this TypeMeta is for an object
// that can be placed in a namespace.
// Implements https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace
func (tm TypeMeta) IsNamespaceable() bool {
for _, k := range notNamespaceableKinds {
if k == tm.Kind {
return false
}
}
return true
}
// NameMeta contains name information.
type NameMeta struct {
// Name is the metadata.name field of a Resource
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Namespace is the metadata.namespace field of a Resource
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
}
// ResourceMeta contains the metadata for a both Resource Type and Resource.
type ResourceMeta struct {
// APIVersion is the apiVersion field of a Resource
APIVersion string `yaml:"apiVersion,omitempty"`
// Kind is the kind field of a Resource
Kind string `yaml:"kind,omitempty"`
TypeMeta `json:",inline" yaml:",inline"`
// ObjectMeta is the metadata field of a Resource
ObjectMeta `yaml:"metadata,omitempty"`
}
// ObjectMeta contains metadata about a Resource
type ObjectMeta struct {
// Name is the metadata.name field of a Resource
Name string `yaml:"name,omitempty"`
// Namespace is the metadata.namespace field of a Resource
Namespace string `yaml:"namespace,omitempty"`
NameMeta `json:",inline" yaml:",inline"`
// Labels is the metadata.labels field of a Resource
Labels map[string]string `yaml:"labels,omitempty"`
// Annotations is the metadata.annotations field of a Resource.
@@ -330,24 +182,23 @@ type ObjectMeta struct {
// the information needed to uniquely identify a resource in a cluster.
func (m *ResourceMeta) GetIdentifier() ResourceIdentifier {
return ResourceIdentifier{
Name: m.Name,
Namespace: m.Namespace,
APIVersion: m.APIVersion,
Kind: m.Kind,
TypeMeta: m.TypeMeta,
NameMeta: m.NameMeta,
}
}
// ResourceIdentifier contains the information needed to uniquely
// identify a resource in a cluster.
type ResourceIdentifier struct {
// Name is the name of the resource as set in metadata.name
Name string `yaml:"name,omitempty"`
// Namespace is the namespace of the resource as set in metadata.namespace
Namespace string `yaml:"namespace,omitempty"`
// ApiVersion is the apiVersion of the resource
APIVersion string `yaml:"apiVersion,omitempty"`
// Kind is the kind of the resource
Kind string `yaml:"kind,omitempty"`
TypeMeta `json:",inline" yaml:",inline"`
NameMeta `json:",inline" yaml:",inline"`
}
// Comments struct is comment yaml comment types
type Comments struct {
LineComment string `yaml:"lineComment,omitempty"`
HeadComment string `yaml:"headComment,omitempty"`
FootComment string `yaml:"footComment,omitempty"`
}
func (r *ResourceIdentifier) GetName() string {
@@ -366,165 +217,6 @@ func (r *ResourceIdentifier) GetKind() string {
return r.Kind
}
var ErrMissingMetadata = fmt.Errorf("missing Resource metadata")
// Field names
const (
AnnotationsField = "annotations"
APIVersionField = "apiVersion"
KindField = "kind"
MetadataField = "metadata"
NameField = "name"
NamespaceField = "namespace"
LabelsField = "labels"
)
// GetMeta returns the ResourceMeta for an RNode
func (rn *RNode) GetMeta() (ResourceMeta, error) {
if IsEmpty(rn) {
return ResourceMeta{}, nil
}
missingMeta := true
n := rn
if n.YNode().Kind == DocumentNode {
// get the content is this is the document node
n = NewRNode(n.Content()[0])
}
// don't decode into the struct directly or it will fail on UTF-8 issues
// which appear in comments
m := ResourceMeta{}
// TODO: consider optimizing this parsing
if f := n.Field(APIVersionField); !IsFieldEmpty(f) {
m.APIVersion = GetValue(f.Value)
missingMeta = false
}
if f := n.Field(KindField); !IsFieldEmpty(f) {
m.Kind = GetValue(f.Value)
missingMeta = false
}
mf := n.Field(MetadataField)
if IsFieldEmpty(mf) {
if missingMeta {
return m, ErrMissingMetadata
}
return m, nil
}
meta := mf.Value
if f := meta.Field(NameField); !IsFieldEmpty(f) {
m.Name = f.Value.YNode().Value
missingMeta = false
}
if f := meta.Field(NamespaceField); !IsFieldEmpty(f) {
m.Namespace = GetValue(f.Value)
missingMeta = false
}
if f := meta.Field(LabelsField); !IsFieldEmpty(f) {
m.Labels = map[string]string{}
_ = f.Value.VisitFields(func(node *MapNode) error {
m.Labels[GetValue(node.Key)] = GetValue(node.Value)
return nil
})
missingMeta = false
}
if f := meta.Field(AnnotationsField); !IsFieldEmpty(f) {
m.Annotations = map[string]string{}
_ = f.Value.VisitFields(func(node *MapNode) error {
m.Annotations[GetValue(node.Key)] = GetValue(node.Value)
return nil
})
missingMeta = false
}
if missingMeta {
return m, ErrMissingMetadata
}
return m, nil
}
// Pipe sequentially invokes each GrepFilter, and passes the result to the next
// GrepFilter.
//
// Analogous to http://www.linfo.org/pipes.html
//
// * rn is provided as input to the first GrepFilter.
// * if any GrepFilter returns an error, immediately return the error
// * if any GrepFilter returns a nil RNode, immediately return nil, nil
// * if all Filters succeed with non-empty results, return the final result
func (rn *RNode) Pipe(functions ...Filter) (*RNode, error) {
// check if rn is nil to make chaining Pipe calls easier
if rn == nil {
return nil, nil
}
var v *RNode
var err error
if rn.value != nil && rn.value.Kind == yaml.DocumentNode {
// the first node may be a DocumentNode containing a single MappingNode
v = &RNode{value: rn.value.Content[0]}
} else {
v = rn
}
// return each fn in sequence until encountering an error or missing value
for _, c := range functions {
v, err = c.Filter(v)
if err != nil || v == nil {
return v, errors.Wrap(err)
}
}
return v, err
}
// PipeE runs Pipe, dropping the *RNode return value.
// Useful for directly returning the Pipe error value from functions.
func (rn *RNode) PipeE(functions ...Filter) error {
_, err := rn.Pipe(functions...)
return errors.Wrap(err)
}
// Document returns the Node RNode for the value. Does not unwrap the node if it is a
// DocumentNodes
func (rn *RNode) Document() *yaml.Node {
return rn.value
}
// YNode returns the yaml.Node value. If the yaml.Node value is a DocumentNode,
// YNode will return the DocumentNode Content entry instead of the DocumentNode.
func (rn *RNode) YNode() *yaml.Node {
if rn == nil || rn.value == nil {
return nil
}
if rn.value.Kind == yaml.DocumentNode {
return rn.value.Content[0]
}
return rn.value
}
// SetYNode sets the yaml.Node value on an RNode.
func (rn *RNode) SetYNode(node *yaml.Node) {
if rn.value == nil || node == nil {
rn.value = node
return
}
*rn.value = *node
}
// AppendToFieldPath appends a field name to the FieldPath.
func (rn *RNode) AppendToFieldPath(parts ...string) {
rn.fieldPath = append(rn.fieldPath, parts...)
}
// FieldPath returns the field path from the Resource root node, to rn.
// Does not include list indexes.
func (rn *RNode) FieldPath() []string {
return rn.fieldPath
}
const (
Trim = "Trim"
Flow = "Flow"
@@ -555,229 +247,3 @@ func String(node *yaml.Node, opts ...string) (string, error) {
}
return val, errors.Wrap(err)
}
// String returns string representation of the RNode
func (rn *RNode) String() (string, error) {
if rn == nil {
return "", nil
}
return String(rn.value)
}
// MustString returns string representation of the RNode or panics if there is an error
func (rn *RNode) MustString() string {
s, err := rn.String()
if err != nil {
panic(err)
}
return s
}
// Content returns Node Content field.
func (rn *RNode) Content() []*yaml.Node {
if rn == nil {
return nil
}
return rn.YNode().Content
}
// Fields returns the list of field names for a MappingNode.
// Returns an error for non-MappingNodes.
func (rn *RNode) Fields() ([]string, error) {
if err := ErrorIfInvalid(rn, yaml.MappingNode); err != nil {
return nil, errors.Wrap(err)
}
var fields []string
for i := 0; i < len(rn.Content()); i += 2 {
fields = append(fields, rn.Content()[i].Value)
}
return fields, nil
}
// Field returns a fieldName, fieldValue pair for MappingNodes.
// Returns nil for non-MappingNodes.
func (rn *RNode) Field(field string) *MapNode {
if rn.YNode().Kind != yaml.MappingNode {
return nil
}
for i := 0; i < len(rn.Content()); i = IncrementFieldIndex(i) {
isMatchingField := rn.Content()[i].Value == field
if isMatchingField {
return &MapNode{Key: NewRNode(rn.Content()[i]), Value: NewRNode(rn.Content()[i+1])}
}
}
return nil
}
// VisitFields calls fn for each field in the RNode.
// Returns an error for non-MappingNodes.
func (rn *RNode) VisitFields(fn func(node *MapNode) error) error {
// get the list of srcFieldNames
srcFieldNames, err := rn.Fields()
if err != nil {
return errors.Wrap(err)
}
// visit each field
for _, fieldName := range srcFieldNames {
if err := fn(rn.Field(fieldName)); err != nil {
return errors.Wrap(err)
}
}
return nil
}
// Elements returns the list of elements in the RNode.
// Returns an error for non-SequenceNodes.
func (rn *RNode) Elements() ([]*RNode, error) {
if err := ErrorIfInvalid(rn, yaml.SequenceNode); err != nil {
return nil, errors.Wrap(err)
}
var elements []*RNode
for i := 0; i < len(rn.Content()); i++ {
elements = append(elements, NewRNode(rn.Content()[i]))
}
return elements, nil
}
// ElementValues returns a list of all observed values for a given field name in a
// list of elements.
// Returns error for non-SequenceNodes.
func (rn *RNode) ElementValues(key string) ([]string, error) {
if err := ErrorIfInvalid(rn, yaml.SequenceNode); err != nil {
return nil, errors.Wrap(err)
}
var elements []string
for i := 0; i < len(rn.Content()); i++ {
field := NewRNode(rn.Content()[i]).Field(key)
if !IsFieldEmpty(field) {
elements = append(elements, field.Value.YNode().Value)
}
}
return elements, nil
}
// Element returns the element in the list which contains the field matching the value.
// Returns nil for non-SequenceNodes or if no Element matches.
func (rn *RNode) Element(key, value string) *RNode {
if rn.YNode().Kind != yaml.SequenceNode {
return nil
}
elem, err := rn.Pipe(MatchElement(key, value))
if err != nil {
return nil
}
return elem
}
// VisitElements calls fn for each element in a SequenceNode.
// Returns an error for non-SequenceNodes
func (rn *RNode) VisitElements(fn func(node *RNode) error) error {
elements, err := rn.Elements()
if err != nil {
return errors.Wrap(err)
}
for i := range elements {
if err := fn(elements[i]); err != nil {
return errors.Wrap(err)
}
}
return nil
}
// AssociativeSequenceKeys is a map of paths to sequences that have associative keys.
// The order sets the precedence of the merge keys -- if multiple keys are present
// in Resources in a list, then the FIRST key which ALL elements in the list have is used as the
// associative key for merging that list.
// Only infer name as a merge key.
var AssociativeSequenceKeys = []string{"name"}
// IsAssociative returns true if the RNode contains an AssociativeSequenceKey as a field.
func (rn *RNode) IsAssociative() bool {
return rn.GetAssociativeKey() != ""
}
// GetAssociativeKey returns the AssociativeSequenceKey used to merge the elements in the
// SequenceNode, or "" if the list is not associative.
func (rn *RNode) GetAssociativeKey() string {
// look for any associative keys in the first element
for _, key := range AssociativeSequenceKeys {
if checkKey(key, rn.Content()) {
return key
}
}
// element doesn't have an associative keys
return ""
}
func (rn *RNode) MarshalJSON() ([]byte, error) {
s, err := rn.String()
if err != nil {
return nil, err
}
if rn.YNode().Kind == SequenceNode {
var a []interface{}
if err := Unmarshal([]byte(s), &a); err != nil {
return nil, err
}
return json.Marshal(a)
}
m := map[string]interface{}{}
if err := Unmarshal([]byte(s), &m); err != nil {
return nil, err
}
return json.Marshal(m)
}
func (rn *RNode) UnmarshalJSON(b []byte) error {
m := map[string]interface{}{}
if err := json.Unmarshal(b, &m); err != nil {
return err
}
c, err := Marshal(m)
if err != nil {
return err
}
r, err := Parse(string(c))
if err != nil {
return err
}
rn.value = r.value
return nil
}
// ConvertJSONToYamlNode parses input json string and returns equivalent yaml node
func ConvertJSONToYamlNode(jsonStr string) (*RNode, error) {
var body map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &body)
if err != nil {
return nil, err
}
yml, err := yaml.Marshal(body)
if err != nil {
return nil, err
}
node, err := Parse(string(yml))
if err != nil {
return nil, err
}
return node, nil
}
// checkKey returns true if all elems have the key
func checkKey(key string, elems []*Node) bool {
count := 0
for i := range elems {
elem := NewRNode(elems[i])
if elem.Field(key) != nil {
count++
}
}
return count == len(elems)
}

View File

@@ -4,236 +4,186 @@
package yaml
import (
"strings"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
// Test that non-UTF8 characters in comments don't cause failures
func TestRNode_GetMeta_UTF16(t *testing.T) {
sr, err := Parse(`apiVersion: rbac.istio.io/v1alpha1
kind: ServiceRole
metadata:
name: wildcard
namespace: default
# If set to [“*”], it refers to all services in the namespace
annotations:
foo: bar
spec:
rules:
# There is one service in default namespace, should not result in a validation error
# If set to [“*”], it refers to all services in the namespace
- services: ["*"]
methods: ["GET", "HEAD"]
`)
if !assert.NoError(t, err) {
t.FailNow()
func TestCopyYNode(t *testing.T) {
ynSub1 := Node{
Kind: 100,
}
actual, err := sr.GetMeta()
if !assert.NoError(t, err) {
t.FailNow()
ynSub2 := Node{
Kind: 200,
}
expected := ResourceMeta{
APIVersion: "rbac.istio.io/v1alpha1",
Kind: "ServiceRole",
ObjectMeta: ObjectMeta{
Name: "wildcard",
Namespace: "default",
Annotations: map[string]string{"foo": "bar"},
},
ynSub3 := Node{
Kind: 300,
}
if !assert.Equal(t, expected, actual) {
t.FailNow()
yn := Node{
Kind: 5000,
Style: 6000,
Tag: "red",
Value: "green",
Anchor: "blue",
Alias: &ynSub3,
Content: []*Node{&ynSub1, &ynSub2},
HeadComment: "apple",
LineComment: "peach",
FootComment: "banana",
Line: 7000,
Column: 8000,
}
ynAddr := &yn
if !reflect.DeepEqual(&yn, ynAddr) {
t.Fatalf("truly %v should equal %v", &yn, ynAddr)
}
ynC := CopyYNode(&yn)
if !reflect.DeepEqual(yn.Content, ynC.Content) {
t.Fatalf("copy content %v is not deep equal to %v", ynC, yn)
}
if !reflect.DeepEqual(&yn, ynC) {
t.Fatalf("\noriginal: %v\n copy: %v\nShould be equal.", yn, ynC)
}
tmp := yn.Content[0].Kind
yn.Content[0].Kind = 666
if reflect.DeepEqual(&yn, ynC) {
t.Fatalf("changing component should break equality")
}
yn.Content[0].Kind = tmp
if !reflect.DeepEqual(&yn, ynC) {
t.Fatalf("should be okay now")
}
yn.Tag = "Different"
if yn.Tag == ynC.Tag {
t.Fatalf("field aliased!")
}
}
func TestRNode_UnmarshalJSON(t *testing.T) {
func TestIsYNodeTaggedNull(t *testing.T) {
if IsYNodeTaggedNull(nil) {
t.Fatalf("nil cannot be tagged null")
}
if IsYNodeTaggedNull(&Node{}) {
t.Fatalf("untagged node is not tagged")
}
if IsYNodeTaggedNull(&Node{Tag: NodeTagFloat}) {
t.Fatalf("float tagged node is not tagged")
}
if !IsYNodeTaggedNull(&Node{Tag: NodeTagNull}) {
t.Fatalf("tagged node is tagged")
}
}
func TestIsYNodeEmptyMap(t *testing.T) {
if IsYNodeEmptyMap(nil) {
t.Fatalf("nil cannot be a map")
}
if IsYNodeEmptyMap(&Node{}) {
t.Fatalf("raw node is not a map")
}
if IsYNodeEmptyMap(&Node{Kind: SequenceNode}) {
t.Fatalf("seq node is not a map")
}
n := &Node{Kind: MappingNode}
if !IsYNodeEmptyMap(n) {
t.Fatalf("empty mapping node is an empty mapping node")
}
n.Content = append(n.Content, &Node{Kind: SequenceNode})
if IsYNodeEmptyMap(n) {
t.Fatalf("a node with content isn't empty")
}
}
func TestIsYNodeEmptySeq(t *testing.T) {
if IsYNodeEmptySeq(nil) {
t.Fatalf("nil cannot be a map")
}
if IsYNodeEmptySeq(&Node{}) {
t.Fatalf("raw node is not a map")
}
if IsYNodeEmptySeq(&Node{Kind: MappingNode}) {
t.Fatalf("map node is not a sequence")
}
n := &Node{Kind: SequenceNode}
if !IsYNodeEmptySeq(n) {
t.Fatalf("empty sequence node is an empty sequence node")
}
n.Content = append(n.Content, &Node{Kind: MappingNode})
if IsYNodeEmptySeq(n) {
t.Fatalf("a node with content isn't empty")
}
}
func TestIsNameSpaceable1(t *testing.T) {
testCases := []struct {
testName string
input string
output string
tm TypeMeta
isNamespaceable bool
}{
{
testName: "simple document",
input: `{"hello":"world"}`,
output: `hello: world`,
tm: TypeMeta{Kind: "ClusterRole"},
isNamespaceable: false,
},
{
testName: "nested structure",
input: `
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "my-deployment",
"namespace": "default"
}
tm: TypeMeta{Kind: "Namespace"},
isNamespaceable: false,
},
{
tm: TypeMeta{Kind: "Deployment"},
isNamespaceable: true,
},
}
for _, tc := range testCases {
if tc.tm.IsNamespaceable() {
if !tc.isNamespaceable {
t.Fatalf("%v is namespaceable, but shouldn't be", tc.tm)
}
} else {
if tc.isNamespaceable {
t.Fatalf("%v is not namespaceable, but should be", tc.tm)
}
}
}
}
`,
output: `
apiVersion: apps/v1
func TestIsNameSpaceable2(t *testing.T) {
testCases := []struct {
yammy string
isNamespaceable bool
}{
{
yammy: `apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
namespace: default
name: bilbo
namespace: middleEarth
`,
},
}
for i := range testCases {
tc := testCases[i]
t.Run(tc.testName, func(t *testing.T) {
instance := &RNode{}
err := instance.UnmarshalJSON([]byte(tc.input))
if !assert.NoError(t, err) {
t.FailNow()
}
actual, err := instance.String()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(tc.output), strings.TrimSpace(actual)) {
t.FailNow()
}
})
}
}
func TestRNode_MarshalJSON(t *testing.T) {
tests := []struct {
name string
ydoc string
want string
}{
{
name: "object",
ydoc: `
hello: world
`,
want: `{"hello":"world"}`,
isNamespaceable: true,
},
{
name: "array",
ydoc: `
- name: s1
- name: s2
yammy: `apiVersion: v1
kind: Namespace
metadata:
name: middleEarth
`,
want: `[{"name":"s1"},{"name":"s2"}]`,
isNamespaceable: false,
},
}
for idx := range tests {
tt := tests[idx]
t.Run(tt.name, func(t *testing.T) {
instance, err := Parse(tt.ydoc)
if !assert.NoError(t, err) {
t.FailNow()
for _, tc := range testCases {
rn, err := Parse(tc.yammy)
if err != nil {
t.Fatalf("unexpected parse error %v from json: %s", err, tc.yammy)
}
meta, err := rn.GetMeta()
if err != nil {
t.Fatalf("unexpected meta error %v", err)
}
if meta.IsNamespaceable() {
if !tc.isNamespaceable {
t.Fatalf("%v is namespaceable, but shouldn't be", meta)
}
actual, err := instance.MarshalJSON()
if !assert.NoError(t, err) {
t.FailNow()
} else {
if tc.isNamespaceable {
t.Fatalf("%v is not namespaceable, but should be", meta)
}
if !assert.Equal(t,
strings.TrimSpace(tt.want), strings.TrimSpace(string(actual))) {
t.FailNow()
}
})
}
}
func TestConvertJSONToYamlNode(t *testing.T) {
inputJSON := `{"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`
expected := `enum:
- allowedValue1
- allowedValue2
maxLength: 15
type: string
`
node, err := ConvertJSONToYamlNode(inputJSON)
if !assert.NoError(t, err) {
t.FailNow()
}
actual, err := node.String()
if !assert.NoError(t, err) {
t.FailNow()
}
assert.Equal(t, expected, actual)
}
func TestIsMissingOrNull(t *testing.T) {
if !IsMissingOrNull(nil) {
t.Fatalf("input: nil")
}
// missing value or null value
if !IsMissingOrNull(NewRNode(nil)) {
t.Fatalf("input: nil value")
}
if IsMissingOrNull(NewScalarRNode("foo")) {
t.Fatalf("input: valid node")
}
// node with NullNodeTag
if !IsMissingOrNull(MakeNullNode()) {
t.Fatalf("input: with NullNodeTag")
}
}
func TestIsEmpty(t *testing.T) {
if !IsEmpty(nil) {
t.Fatalf("input: nil")
}
// missing value or null value
if !IsEmpty(NewRNode(nil)) {
t.Fatalf("input: nil value")
}
// not array or map
if IsEmpty(NewScalarRNode("foo")) {
t.Fatalf("input: not array or map")
}
}
func TestIsEmpty_Arrays(t *testing.T) {
node := NewListRNode()
// empty array. empty array is not expected as empty
if IsEmpty(node) {
t.Fatalf("input: empty array")
}
// array with 1 item
node = NewListRNode("foo")
if IsEmpty(node) {
t.Fatalf("input: array with 1 item")
}
// delete the item in array
node.value.Content = nil
if IsEmpty(node) {
t.Fatalf("input: empty array")
}
}
func TestIsEmpty_Maps(t *testing.T) {
node := NewMapRNode(nil)
// empty map
if !IsEmptyMap(node) {
t.Fatalf("input: empty map")
}
// map with 1 item
node = NewMapRNode(&map[string]string{
"foo": "bar",
})
if IsEmptyMap(node) {
t.Fatalf("input: map with 1 item")
}
// delete the item in map
node.value.Content = nil
if !IsEmptyMap(node) {
t.Fatalf("input: empty map")
}
}
}

View File

@@ -51,7 +51,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
if err != nil {
return nil, err
}
if yaml.IsEmpty(val) || yaml.IsEmptyMap(val) {
if yaml.IsMissingOrNull(val) || yaml.IsEmptyMap(val) {
_, err = dest.Pipe(yaml.ElementSetter{Key: key, Value: value})
if err != nil {
return nil, err
@@ -74,7 +74,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
}
}
// field is empty
if yaml.IsEmpty(dest) {
if yaml.IsMissingOrNull(dest) {
return nil, nil
}
return dest, nil
@@ -97,7 +97,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
if err != nil {
return nil, err
}
if yaml.IsEmpty(val) {
if yaml.IsMissingOrNull(val) {
_, err = dest.Pipe(yaml.ElementSetter{Key: key, Value: value})
if err != nil {
return nil, err
@@ -121,7 +121,7 @@ func (l *Walker) walkAssociativeSequence() (*yaml.RNode, error) {
}
// field is empty
if yaml.IsEmpty(dest) {
if yaml.IsMissingOrNull(dest) {
return nil, nil
}
return dest, nil

View File

@@ -27,11 +27,12 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
// recursively set the field values on the map
for _, key := range l.fieldNames() {
var res *yaml.RNode
var keys []*yaml.RNode
if l.VisitKeysAsScalars {
// visit the map keys as if they were scalars,
// this is necessary if doing things such as copying
// comments
var keys []*yaml.RNode
for i := range l.Sources {
// construct the sources from the keys
if l.Sources[i] == nil {
@@ -39,7 +40,7 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
continue
}
field := l.Sources[i].Field(key)
if field == nil || yaml.IsEmpty(field.Key) {
if field == nil || yaml.IsMissingOrNull(field.Key) {
keys = append(keys, nil)
continue
}
@@ -47,7 +48,7 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
}
// visit the sources as a scalar
// keys don't have any schema --pass in nil
_, err := l.Visitor.VisitScalar(keys, nil)
res, err = l.Visitor.VisitScalar(keys, nil)
if err != nil {
return nil, err
}
@@ -72,8 +73,23 @@ func (l Walker) walkMap() (*yaml.RNode, error) {
return nil, err
}
// transfer the comments of res to dest node
var comments yaml.Comments
if !yaml.IsMissingOrNull(res) {
comments = yaml.Comments{
LineComment: res.YNode().LineComment,
HeadComment: res.YNode().HeadComment,
FootComment: res.YNode().FootComment,
}
if len(keys) > 0 && !yaml.IsMissingOrNull(keys[DestIndex]) {
keys[DestIndex].YNode().HeadComment = res.YNode().HeadComment
keys[DestIndex].YNode().LineComment = res.YNode().LineComment
keys[DestIndex].YNode().FootComment = res.YNode().FootComment
}
}
// this handles empty and non-empty values
_, err = dest.Pipe(yaml.FieldSetter{Name: key, Value: val})
_, err = dest.Pipe(yaml.FieldSetter{Name: key, Comments: comments, Value: val})
if err != nil {
return nil, err
}
@@ -148,7 +164,7 @@ func (l Walker) fieldValue(fieldName string) ([]*yaml.RNode, *openapi.ResourceSc
field := l.Sources[i].Field(fieldName)
f, s := l.valueIfPresent(field)
fields = append(fields, f)
if sch == nil && !s.IsEmpty() {
if sch == nil && !s.IsMissingOrNull() {
sch = s
}
}

View File

@@ -40,7 +40,7 @@ type Walker struct {
func (l Walker) Kind() yaml.Kind {
for _, s := range l.Sources {
if !yaml.IsEmpty(s) {
if !yaml.IsMissingOrNull(s) {
return s.YNode().Kind
}
}
@@ -83,7 +83,7 @@ func (l Walker) Walk() (*yaml.RNode, error) {
func (l Walker) GetSchema() *openapi.ResourceSchema {
for i := range l.Sources {
r := l.Sources[i]
if yaml.IsEmpty(r) {
if yaml.IsMissingOrNull(r) {
continue
}
@@ -106,7 +106,7 @@ func (l Walker) GetSchema() *openapi.ResourceSchema {
}
for i := range l.Sources {
r := l.Sources[i]
if yaml.IsEmpty(r) {
if yaml.IsMissingOrNull(r) {
continue
}

View File

@@ -4,7 +4,7 @@ go 1.14
require (
sigs.k8s.io/kustomize/api v0.5.1
sigs.k8s.io/kustomize/kyaml v0.4.2
sigs.k8s.io/kustomize/kyaml v0.5.0
sigs.k8s.io/yaml v1.2.0
)

View File

@@ -411,8 +411,6 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -501,8 +499,8 @@ 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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
@@ -523,8 +521,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.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -411,8 +411,6 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -501,8 +499,8 @@ 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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
@@ -523,8 +521,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.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -411,8 +411,6 @@ golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -501,8 +499,8 @@ 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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
@@ -523,8 +521,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.4.2 h1:9/Tb90gnThv4vgUldZOLnrT+9Esdh7+Og2UIq024Ykg=
sigs.k8s.io/kustomize/kyaml v0.4.2/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw=
sigs.k8s.io/kustomize/kyaml v0.5.0 h1:xufpSxgpugQxtd0aN1ZsWnr3Kj0fpAi7GN4dnEs4oPg=
sigs.k8s.io/kustomize/kyaml v0.5.0/go.mod h1:bEzbO5pN9OvlEeCLvFHo8Pu7SA26Herc2m60UeWZBdI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

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