Compare commits

...

95 Commits

Author SHA1 Message Date
monopole
40bf89abcd Pin to cmd/config v0.8.8 2021-01-17 08:40:13 -08:00
Jeff Regan
7f548eddd0 Merge pull request #3474 from kubernetes-sigs/pinToKyamlAndCliUtils
Pin to kyaml v0.10.6 and cli-utils v0.22.4
2021-01-17 07:17:43 -08:00
monopole
86e9983bb7 Pin to kyaml v0.10.6 and cli-utils v0.22.4 2021-01-17 06:55:10 -08:00
Jeff Regan
cbbcfde99d Update multiplepatch_test.go 2021-01-16 20:43:57 -08:00
Jeff Regan
304a9e57ee Update factory_test.go 2021-01-16 20:36:46 -08:00
Jeff Regan
f23f26aa05 Update factory_test.go 2021-01-16 20:24:39 -08:00
Jeff Regan
720857623f Merge pull request #3473 from monopole/doImportantStuff
Fix unfiled bug in port replacement where ports were being quoted.
2021-01-16 18:02:06 -08:00
monopole
065c2b861a Fix unfiled bug; don't quote port numbers. 2021-01-16 16:56:48 -08:00
monopole
2a16af80bf Simplify, document and add more tests to var replacement. 2021-01-16 16:48:26 -08:00
Jeff Regan
81d324c68c Merge pull request #3472 from monopole/issue3449
Reproduce issue #3449
2021-01-16 16:47:45 -08:00
monopole
b8702561ef Add test for issue 3449 2021-01-16 16:45:13 -08:00
Jeff Regan
ea039b36bc Merge pull request #3471 from monopole/mergeExpansionRefvar
Merge expansion package into refvar package.
2021-01-16 13:54:32 -08:00
monopole
561cef1d5c Merge expansion package into refvar package. 2021-01-16 13:50:56 -08:00
Jeff Regan
62c5e424a6 Merge pull request #3470 from monopole/annotateIssue3304
Annotate code with decisions on issue 3304 in kyaml conversion
2021-01-16 09:09:49 -08:00
monopole
45b1bf17d3 Annotate decisions on issue 3304 in api. 2021-01-16 08:08:52 -08:00
monopole
11dce34407 Annotate decisions on issue 3304 in plugins. 2021-01-16 08:08:39 -08:00
Kubernetes Prow Robot
550a89295a Merge pull request #3469 from monopole/labelAndAnnotationQuoting
Always quote non-string values in labels and annotations
2021-01-15 18:43:43 -08:00
monopole
8083b3607f Harden anno transformer test. 2021-01-15 17:37:46 -08:00
monopole
cb42142161 Fix api tests that accomodated bad label and anno quoting. 2021-01-15 17:37:46 -08:00
monopole
cb59e0ef5f Always tag label and annotations values as strings. 2021-01-15 17:37:46 -08:00
Jeff Regan
1a4a9fcdaf Update go.mod 2021-01-15 17:33:00 -08:00
Kubernetes Prow Robot
eb8dc5e20a Merge pull request #3462 from mikhail-nikitin/master
$patch: delete of not existing elements
2021-01-15 14:23:43 -08:00
Jeff Regan
0fb30a1010 In plugin tests, yell FAILURE on failure. 2021-01-15 13:58:09 -08:00
Jeff Regan
fdfdfa9e4d Update go.mod 2021-01-15 13:56:18 -08:00
Jeff Regan
6042aca7a4 Update CalvinDuplicator.go 2021-01-15 13:33:46 -08:00
Jeff Regan
94962c8bac Update CalvinDuplicator_test.go 2021-01-15 13:10:40 -08:00
Mikhail Nikitin
f6ddea435c Make test file paths consistent and relative
Signed-off-by: Mikhail Nikitin <mikhail.nikitin@ispringsolutions.com>
2021-01-15 22:50:23 +03:00
Jeff Regan
a9d4b7615f Merge pull request #3466 from monopole/renameAnnotations
Rename id annotations to build annotations.
2021-01-15 11:27:49 -08:00
Jeff Regan
822cac26f9 Merge pull request #3467 from monopole/calvinDuplicator
Add calvin duplicator example plugin.
2021-01-15 07:58:11 -08:00
monopole
97eedc8a43 Add calvin duplicator example plugin. 2021-01-15 07:22:46 -08:00
monopole
2cb972de3b Rename id annotations to build annotations. 2021-01-15 06:43:13 -08:00
Jeff Regan
79d0d6b5e1 Merge pull request #3465 from monopole/pdbTest
Add test covering pod disruption budget treatment.
2021-01-15 06:38:38 -08:00
monopole
fabaf35c72 Add test covering pod disruption budget treatment. 2021-01-15 06:09:52 -08:00
Mikhail Nikitin
e13f8803eb Add test case of deleting not existing elements 2021-01-15 00:08:49 +03:00
Jeff Regan
64ffbcb15d Merge pull request #3461 from monopole/anotherTest
Add another test covering fix of 3412
2021-01-14 11:52:56 -08:00
monopole
b41df2293b Add another test covering fix of 3412 2021-01-14 11:32:34 -08:00
Kubernetes Prow Robot
e3fcec122a Merge pull request #3460 from monopole/fix3412
Fix 3412, retaining quotes in configmap data fields
2021-01-14 11:25:32 -08:00
monopole
1edf9b630c Update configmap test with quotes fixed. 2021-01-14 11:01:37 -08:00
monopole
7c6bf2e21d When merging configmaps, retain proper quoting. 2021-01-14 11:01:06 -08:00
monopole
b3fc306f6a Move some code to make it reusable without import cycles. 2021-01-14 10:29:51 -08:00
Jeff Regan
e92d048af2 Merge pull request #3441 from natasha41575/namePrefixNamespaceBehavior
Update name references by checking both the original and current namespaces
2021-01-13 16:21:05 -08:00
Kubernetes Prow Robot
f76059b824 Merge pull request #3454 from monopole/evenMoreTests
Add more tests and explain some quoting behavior.
2021-01-13 16:05:03 -08:00
monopole
bb41d018b5 Add more tests and explain some strange quotes. 2021-01-13 15:49:12 -08:00
Kubernetes Prow Robot
cf8815b0a0 Merge pull request #3453 from Shell32-Natsu/wnode-field-value
fix GetFieldValue cannot handle slice index
2021-01-13 15:07:03 -08:00
Donny Xia
64beee22e9 fix GetFieldValue cannot handle slice index 2021-01-13 14:14:01 -08:00
Jeff Regan
79afd219a5 Merge pull request #3450 from monopole/anotherTest
Add a test that only loads an annotated resource.
2021-01-13 11:27:14 -08:00
monopole
c68cf40d75 Add a test that only loads an annotated resource. 2021-01-13 10:56:50 -08:00
Kubernetes Prow Robot
c7337a7d87 Merge pull request #3445 from KnVerey/resource_list_empty_items
[kio] Unwrap ResourceList with a functionConfig but no items
2021-01-12 17:04:35 -08:00
Jeff Regan
875e265e5d Merge pull request #3372 from natasha41575/AddNameAnnotations
Refactor resource to use annotations in the yaml instead of fields in resource struct
2021-01-12 16:38:48 -08:00
Katrina Verey
bdbfb28139 Unwrap ResourceList with a functionConfig but no items 2021-01-12 16:13:40 -08:00
Natasha Sarkar
d54bc674f2 Update name references by checking both the original and current namespaces 2021-01-11 18:08:52 -08:00
Natasha Sarkar
bd4580d73a Manage name changes (prefix/suffix) via YAML annotations rather than via in-memory-only fields. 2021-01-11 13:08:45 -08:00
Kubernetes Prow Robot
ea5d08bac5 Merge pull request #3438 from monopole/fix3424
Fix 3424 by avoiding a JSON round trip
2021-01-11 10:18:24 -08:00
monopole
14a1a0e4a8 Fix 3424 by avoiding a JSON round trip 2021-01-10 20:39:01 -08:00
Jeff Regan
497e8038a3 Merge pull request #3439 from monopole/clarifyErrorMessage
Clarify var-related error message.
2021-01-10 20:38:39 -08:00
monopole
44b5acad51 Clarify var-related error message. 2021-01-10 20:35:55 -08:00
Jeff Regan
e5e19f7c09 Merge pull request #3431 from Shell32-Natsu/newline
keep \n in the end of resource yaml
2021-01-10 12:51:36 -08:00
Jeff Regan
a03843dfc7 Merge pull request #3437 from monopole/regressionTestsForKyaml
In kyaml, loosen interpretation of string node and add tests.
2021-01-10 12:28:30 -08:00
monopole
b7cce27d40 In kyaml, loosen interpretation of string node and add tests. 2021-01-10 12:08:50 -08:00
Jeff Regan
126f5481f3 Merge pull request #3436 from monopole/addTestsAroundVarRefTransformer
Add var ref replacement tests and more doc.
2021-01-10 09:38:30 -08:00
monopole
30dcf38609 Add var ref replacement tests and more doc. 2021-01-10 09:16:52 -08:00
Jeff Regan
1a2779b2c3 Merge pull request #3434 from monopole/reduceComplexityInNameReferenceTransformer
Reduce complexity in NameReferenceTransformer.
2021-01-10 07:12:23 -08:00
monopole
658b62c6f1 Reduce complexity in NameReferenceTransformer. 2021-01-10 06:56:06 -08:00
Jeff Regan
cf0bb49610 Merge pull request #3433 from monopole/anotherTowards3412
Improve handling of empty resource maps.
2021-01-09 07:20:19 -08:00
monopole
c2fbb709da Don't swallow error in SM patch and use new RNode Map method. 2021-01-09 07:00:21 -08:00
monopole
1a002005c1 Add RNode.Map method and test to help decoding. 2021-01-09 06:57:01 -08:00
Jeff Regan
4f468fcc90 Merge pull request #3432 from monopole/towards3412
Short circuit anno/label transformer for performance.
2021-01-08 18:53:47 -08:00
monopole
769f65d6c4 Short circuit anno/label transformer for performance. 2021-01-08 18:02:58 -08:00
Donny Xia
378eaedc82 keep \n in the end of resource yaml 2021-01-08 15:36:36 -08:00
Jeff Regan
6f2f401f6b Merge pull request #3428 from monopole/confineApplyToJson
Confine calls to ApplyToJSON.
2021-01-07 20:53:55 -08:00
monopole
614e853db3 Confine calls to ApplyToJSON. 2021-01-07 20:31:10 -08:00
Jeff Regan
33be04db45 Merge pull request #3427 from monopole/avoidCycle
Move plugin lister to avoid import cycle.
2021-01-07 19:27:45 -08:00
monopole
8c6a9f6495 Move plugin lister to avoid import cycle. 2021-01-07 18:46:04 -08:00
Jeff Regan
03b2fff0ee Merge pull request #3425 from monopole/issue3424
Add test for issue 3424
2021-01-07 15:55:47 -08:00
monopole
69cade143f Add test for issue 3424 2021-01-06 16:23:12 -08:00
Jeff Regan
90f45651d1 Merge pull request #3416 from HansK-p/Fix-link-to-lugins-doc
Updated link to Kustomize plugins documentation
2021-01-06 15:49:19 -08:00
Hans Kristian Nordengen
1b740034f7 Updated link to Kustomize plugins documentation 2021-01-03 19:44:32 +01:00
Jeff Regan
a2d8e686de Merge pull request #3411 from monopole/unpinApiKyamlCmdConfig
Unpin kyaml, cmd/config, api
2020-12-29 18:17:48 -08:00
jregan
ce2ab487a5 Unpin kyaml, cmd/config, api 2020-12-29 16:18:12 -08:00
Jeff Regan
7439f1809e Merge pull request #3410 from monopole/pinToApiv0.7.1
Pin to api/v0.7.1 (--enableKyaml=true)
2020-12-29 09:43:42 -08:00
jregan
6977c83a83 Pin to api/v0.7.1 (--enableKyaml=true) 2020-12-29 09:24:25 -08:00
Jeff Regan
7b9eb05058 Merge pull request #3408 from monopole/enableKyamlForApiv0.7.1
Set FlagEnableKyamlDefaultValue = true
2020-12-29 08:36:33 -08:00
jregan
e2806a09fd Set FlagEnableKyamlDefaultValue = true 2020-12-29 08:17:45 -08:00
Jeff Regan
f30fea4c07 Merge pull request #3407 from monopole/pinToApiv0.6.8
Pin to api/v0.6.8
2020-12-29 07:44:13 -08:00
jregan
8732671919 Pin to api/v0.6.8 2020-12-29 07:27:11 -08:00
Jeff Regan
07cada36fa Merge pull request #3406 from monopole/pinToCmdConfig_v0.8.7
Pin to cmd/confg v0.8.7
2020-12-29 07:11:12 -08:00
jregan
0d6b232b49 Pin to cmd/confg v0.8.7 2020-12-29 06:55:07 -08:00
Jeff Regan
61455fe489 Merge pull request #3404 from monopole/pinKyamlv0.10.5
Pin to kyaml v0.10.5
2020-12-28 20:50:59 -08:00
jregan
c63ed033ad Pin to kyaml v0.10.5
ALLOW_MODULE_SPAN
2020-12-28 20:30:22 -08:00
Jeff Regan
455bd0c563 Merge pull request #3403 from monopole/betterPin
Unpin versioned and unversioned pins.
2020-12-28 18:11:39 -08:00
jregan
9ad4b1ddca Unpin versioned and unversioned pins. 2020-12-28 18:10:53 -08:00
Jeff Regan
d529eb8777 Merge pull request #3402 from monopole/upgradeGopkg.in_yaml.v3
Pin to gopkg.in/yaml.v3 v3.0.0-20200313102051
2020-12-28 18:02:41 -08:00
jregan
f7b2f0c067 Pin to gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
This is the last commit in yaml.v3 before

  ae27a74434

which changed the indentation of sequence.

That change has has large downstream impact on tests in the kustomize
repo.  To upgrade beyond this point in yaml.v3 means many changes to
indentation in "expected" values in tests.  That should be done in a
PR dedicated to that purpose, after specific consideration the change.

ALLOW_MODULE_SPAN
2020-12-28 17:28:01 -08:00
Jeff Regan
ff6b337ebe Update README.md 2020-12-28 14:06:43 -08:00
Jeff Regan
76f05f3a40 Update cli-utils release instructions. 2020-12-28 13:57:21 -08:00
163 changed files with 2934 additions and 876 deletions

View File

@@ -7,7 +7,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/annotations"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -25,11 +24,14 @@ func (p *AnnotationsTransformerPlugin) Config(
}
func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
if len(p.Annotations) == 0 {
return nil
}
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(annotations.Filter{
err := r.ApplyFilter(annotations.Filter{
Annotations: p.Annotations,
FsSlice: p.FieldSpecs,
}, r)
})
if err != nil {
return err
}

View File

@@ -28,6 +28,7 @@ func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
if err != nil {
return err
}
res.SetOriginalName(res.GetName(), false)
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
}
}

View File

@@ -11,7 +11,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/imagetag"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -32,17 +31,17 @@ func (p *ImageTagTransformerPlugin) Config(
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
// traverse all fields at first
err := filtersutil.ApplyToJSON(imagetag.LegacyFilter{
err := r.ApplyFilter(imagetag.LegacyFilter{
ImageTag: p.ImageTag,
}, r)
})
if err != nil {
return err
}
// then use user specified field specs
err = filtersutil.ApplyToJSON(imagetag.Filter{
err = r.ApplyFilter(imagetag.Filter{
ImageTag: p.ImageTag,
FsSlice: p.FieldSpecs,
}, r)
})
if err != nil {
return err
}

View File

@@ -7,7 +7,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/labels"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -25,11 +24,14 @@ func (p *LabelTransformerPlugin) Config(
}
func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
if len(p.Labels) == 0 {
return nil
}
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(labels.Filter{
err := r.ApplyFilter(labels.Filter{
Labels: p.Labels,
FsSlice: p.FieldSpecs,
}, r)
})
if err != nil {
return err
}

View File

@@ -9,7 +9,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/namespace"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -35,10 +34,11 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
// Don't mutate empty objects?
continue
}
err := filtersutil.ApplyToJSON(namespace.Filter{
r.SetOriginalNs(r.GetNamespace(), false)
err := r.ApplyFilter(namespace.Filter{
Namespace: p.Namespace,
FsSlice: p.FieldSpecs,
}, r)
})
if err != nil {
return err
}

View File

@@ -12,7 +12,6 @@ import (
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -79,9 +78,9 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
return err
}
for _, res := range resources {
err = filtersutil.ApplyToJSON(patchjson6902.Filter{
err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.JsonOp,
}, res)
})
if err != nil {
return err
}

View File

@@ -12,7 +12,6 @@ import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -105,9 +104,10 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
return err
}
for _, res := range resources {
err = filtersutil.ApplyToJSON(patchjson6902.Filter{
res.SetOriginalName(res.GetName(), false)
err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.Patch,
}, res)
})
if err != nil {
return err
}

View File

@@ -10,7 +10,6 @@ import (
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -67,14 +66,18 @@ func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
// this will add a prefix and a suffix
// to the resource even if those are
// empty
r.AddNamePrefix(p.Prefix)
r.AddNameSuffix(p.Suffix)
if p.Prefix != "" || p.Suffix != "" {
r.SetOriginalName(r.GetName(), false)
}
}
err := filtersutil.ApplyToJSON(prefixsuffix.Filter{
err := r.ApplyFilter(prefixsuffix.Filter{
Prefix: p.Prefix,
Suffix: p.Suffix,
FieldSpec: fs,
}, r)
})
if err != nil {
return err
}

View File

@@ -7,8 +7,6 @@ import (
"fmt"
"sigs.k8s.io/kustomize/api/filters/replicacount"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
@@ -42,10 +40,10 @@ func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
// There are redundant checks in the filter
// that we'll live with until resolution of
// https://github.com/kubernetes-sigs/kustomize/issues/2506
err := filtersutil.ApplyToJSON(replicacount.Filter{
err := r.ApplyFilter(replicacount.Filter{
Replica: p.Replica,
FieldSpec: fs,
}, r)
})
if err != nil {
return err
}

View File

@@ -13,7 +13,6 @@ import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -119,15 +118,15 @@ func (p *ValueAddTransformerPlugin) Transform(m resmap.ResMap) (err error) {
// TODO: consider t.NotSelector if implemented
for _, res := range resources {
if t.FieldPath == types.MetadataNamespacePath {
err = filtersutil.ApplyToJSON(namespace.Filter{
err = res.ApplyFilter(namespace.Filter{
Namespace: p.Value,
}, res)
})
} else {
err = filtersutil.ApplyToJSON(valueadd.Filter{
err = res.ApplyFilter(valueadd.Filter{
Value: p.Value,
FieldPath: t.FieldPath,
FilePathPosition: t.FilePathPosition,
}, res)
})
}
if err != nil {
return err

View File

@@ -24,7 +24,7 @@ type Filter struct {
var _ kio.Filter = Filter{}
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
keys := filtersutil.SortedMapKeys(f.Annotations)
keys := yaml.SortedMapKeys(f.Annotations)
_, err := kio.FilterAll(yaml.FilterFunc(
func(node *yaml.RNode) (*yaml.RNode, error) {
for _, k := range keys {

View File

@@ -28,7 +28,9 @@ metadata:
`)}},
Filters: []kio.Filter{Filter{
Annotations: map[string]string{
"foo": "bar",
"foo": "bar",
"booleanValue": "true",
"numberValue": "42",
},
FsSlice: fss,
}},
@@ -44,12 +46,16 @@ metadata:
// metadata:
// name: instance
// annotations:
// booleanValue: "true"
// foo: bar
// numberValue: "42"
// ---
// apiVersion: example.com/v1
// kind: Bar
// metadata:
// name: instance
// annotations:
// booleanValue: "true"
// foo: bar
// numberValue: "42"
}

View File

@@ -1,21 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filtersutil
import (
"sort"
)
// SortedMapKeys returns a sorted slice of keys to the given map.
// Writing this function never gets old.
func SortedMapKeys(m map[string]string) []string {
keys := make([]string, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}

View File

@@ -25,7 +25,7 @@ type Filter struct {
var _ kio.Filter = Filter{}
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
keys := filtersutil.SortedMapKeys(f.Labels)
keys := yaml.SortedMapKeys(f.Labels)
_, err := kio.FilterAll(yaml.FilterFunc(
func(node *yaml.RNode) (*yaml.RNode, error) {
for _, k := range keys {

View File

@@ -6,7 +6,6 @@ import (
"strings"
"sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
@@ -16,22 +15,36 @@ import (
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Filter will update the name reference
// Filter updates a name references.
type Filter struct {
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
Referrer *resource.Resource
Target resid.Gvk
// Referrer is the object that refers to something else by a name,
// a name that this filter seeks to update.
Referrer *resource.Resource
// NameFieldToUpdate is the field in the Referrer that holds the
// name requiring an update.
NameFieldToUpdate types.FieldSpec `json:"nameFieldToUpdate,omitempty" yaml:"nameFieldToUpdate,omitempty"`
// Source of the new value for the name (in its name field).
ReferralTarget resid.Gvk
// Set of resources to hunt through to find the ReferralTarget.
ReferralCandidates resmap.ResMap
isRoleRef bool
}
// At time of writing, in practice this is called with a slice with only
// one entry, the node also referred to be the resource in the Referrer field.
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
}
// The node passed in here is the same node as held in Referrer, and
// that's how the referrer's name field is updated.
// However, this filter still needs the extra methods on Referrer
// to consult things like the resource Id, its namespace, etc.
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
err := node.PipeE(fieldspec.Filter{
FieldSpec: f.FieldSpec,
FieldSpec: f.NameFieldToUpdate,
SetValue: f.set,
})
return node, err
@@ -41,58 +54,102 @@ func (f Filter) set(node *yaml.RNode) error {
if yaml.IsMissingOrNull(node) {
return nil
}
if strings.HasSuffix(f.FieldSpec.Path, "roleRef/name") {
f.isRoleRef = true
}
switch node.YNode().Kind {
case yaml.ScalarNode:
return f.setScalar(node)
case yaml.MappingNode:
// Kind: ValidatingWebhookConfiguration
// FieldSpec is webhooks/clientConfig/service
return f.setMapping(node)
case yaml.SequenceNode:
return f.setSequence(node)
return applyFilterToSeq(seqFilter{
setScalarFn: f.setScalar,
setMappingFn: f.setMapping,
}, node)
default:
return fmt.Errorf(
"node is expected to be either a string or a slice of string or a map of string")
}
}
func (f Filter) setSequence(node *yaml.RNode) error {
return applyFilterToSeq(seqFilter{
setScalarFn: f.setScalar,
setMappingFn: f.setMapping,
}, node)
}
// Replace name field within a map RNode and leverage the namespace field.
func (f Filter) setMapping(node *yaml.RNode) error {
return setNameAndNs(
node,
f.Referrer,
f.Target,
f.ReferralCandidates,
f.isRoleRef,
)
if node.YNode().Kind != yaml.MappingNode {
return fmt.Errorf("expect a mapping node")
}
nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"})
if err != nil || nameNode == nil {
return fmt.Errorf("cannot find field 'name' in node")
}
namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"})
if err != nil {
return fmt.Errorf("error when find field 'namespace'")
}
// name will not be updated if the namespace doesn't match
subset := f.ReferralCandidates.Resources()
if namespaceNode != nil {
namespace := namespaceNode.YNode().Value
bynamespace := f.ReferralCandidates.GroupedByOriginalNamespace()
if _, ok := bynamespace[namespace]; !ok {
bynamespace = f.ReferralCandidates.GroupedByCurrentNamespace()
if _, ok := bynamespace[namespace]; !ok {
return nil
}
}
subset = bynamespace[namespace]
}
oldName := nameNode.YNode().Value
res, err := f.selectReferral(oldName, subset)
if err != nil || res == nil {
// Nil res means nothing to do.
return err
}
f.recordTheReferral(res)
if res.GetName() == oldName && res.GetNamespace() == "" {
// The name has not changed, nothing to do.
return nil
}
err = node.PipeE(yaml.FieldSetter{
Name: "name",
StringValue: res.GetName(),
})
if err != nil {
return err
}
if res.GetNamespace() != "" {
// We don't want value "" to replace value "default" since
// the empty string is handled as a wild card here not default namespace
// by kubernetes.
err = node.PipeE(yaml.FieldSetter{
Name: "namespace",
StringValue: res.GetNamespace(),
})
}
return err
}
func (f Filter) setScalar(node *yaml.RNode) error {
newValue, err := getSimpleNameField(
node.YNode().Value,
f.Referrer,
f.Target,
f.ReferralCandidates,
f.ReferralCandidates.Resources(),
f.isRoleRef,
)
if err != nil {
res, err := f.selectReferral(
node.YNode().Value, f.ReferralCandidates.Resources())
if err != nil || res == nil {
// Nil res means nothing to do.
return err
}
err = filtersutil.SetScalar(newValue)(node)
if err != nil {
return err
f.recordTheReferral(res)
if res.GetName() == node.YNode().Value {
// The name has not changed, nothing to do.
return nil
}
return nil
return node.PipeE(yaml.FieldSetter{StringValue: res.GetName()})
}
// In the resource, make a note that it is referred to by the referrer.
func (f Filter) recordTheReferral(res *resource.Resource) {
res.AppendRefBy(f.Referrer.CurId())
}
func (f Filter) isRoleRef() bool {
return strings.HasSuffix(f.NameFieldToUpdate.Path, "roleRef/name")
}
// getRoleRefGvk returns a Gvk in the roleRef field. Return error
@@ -114,14 +171,16 @@ func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) {
return nil, err
}
if apiGroup.IsNil() {
return nil, fmt.Errorf("apiGroup cannot be found in roleRef %s", roleRef.MustString())
return nil, fmt.Errorf(
"apiGroup cannot be found in roleRef %s", roleRef.MustString())
}
kind, err := roleRef.Pipe(yaml.Lookup("kind"))
if err != nil {
return nil, err
}
if kind.IsNil() {
return nil, fmt.Errorf("kind cannot be found in roleRef %s", roleRef.MustString())
return nil, fmt.Errorf(
"kind cannot be found in roleRef %s", roleRef.MustString())
}
return &resid.Gvk{
Group: apiGroup.YNode().Value,
@@ -129,19 +188,17 @@ func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) {
}, nil
}
func filterReferralCandidates(
referrer *resource.Resource,
matches []*resource.Resource,
target resid.Gvk,
) []*resource.Resource {
func (f Filter) filterReferralCandidates(
matches []*resource.Resource) []*resource.Resource {
var ret []*resource.Resource
for _, m := range matches {
// If target kind is not ServiceAccount, we shouldn't consider condidates which
// doesn't have same namespace.
if target.Kind != "ServiceAccount" && m.GetNamespace() != referrer.GetNamespace() {
if f.ReferralTarget.Kind != "ServiceAccount" &&
m.GetNamespace() != f.Referrer.GetNamespace() {
continue
}
if !referrer.PrefixesSuffixesEquals(m) {
if !f.Referrer.PrefixesSuffixesEquals(m) {
continue
}
ret = append(ret, m)
@@ -150,72 +207,57 @@ func filterReferralCandidates(
}
// selectReferral picks the referral among a subset of candidates.
// It returns the current name and namespace of the selected candidate.
// Note that the content of the referricalCandidateSubset slice is most of the time
// identical to the referralCandidates resmap. Still in some cases, such
// The content of the candidateSubset slice is most of the time
// identical to the ReferralCandidates resmap. Still in some cases, such
// as ClusterRoleBinding, the subset only contains the resources of a specific
// namespace.
func selectReferral(
func (f Filter) selectReferral(
oldName string,
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource,
isRoleRef bool) (string, string, error) {
candidateSubset []*resource.Resource) (*resource.Resource, error) {
var roleRefGvk *resid.Gvk
if isRoleRef {
if f.isRoleRef() {
var err error
roleRefGvk, err = getRoleRefGvk(referrer)
roleRefGvk, err = getRoleRefGvk(f.Referrer)
if err != nil {
return "", "", err
return nil, err
}
}
for _, res := range referralCandidateSubset {
for _, res := range candidateSubset {
if res.GetOriginalName() != oldName {
continue
}
id := res.OrgId()
if !id.IsSelected(&f.ReferralTarget) {
continue
}
// If the we are processing a roleRef, the apiGroup and Kind in the
// roleRef are needed to be considered.
if (!isRoleRef || id.IsSelected(roleRefGvk)) &&
id.IsSelected(&target) && res.GetOriginalName() == oldName {
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
// If there's more than one match,
// filter the matches by prefix and suffix
if len(matches) > 1 {
filteredMatches := filterReferralCandidates(referrer, matches, target)
if len(filteredMatches) > 1 {
return "", "", fmt.Errorf(
"multiple matches for %s:\n %v",
id, getIds(filteredMatches))
}
// Check is the match the resource we are working on
if len(filteredMatches) == 0 || res != filteredMatches[0] {
continue
}
}
// In the resource, note that it is referenced
// by the referrer.
res.AppendRefBy(referrer.CurId())
// Return transformed name of the object,
// complete with prefixes, hashes, etc.
return res.GetName(), res.GetNamespace(), nil
if f.isRoleRef() && !id.IsSelected(roleRefGvk) {
continue
}
matches := f.ReferralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
// If there's more than one match,
// filter the matches by prefix and suffix
if len(matches) > 1 {
filteredMatches := f.filterReferralCandidates(matches)
if len(filteredMatches) > 1 {
return nil, fmt.Errorf(
"multiple matches for %s:\n %v",
id, getIds(filteredMatches))
}
// Check is the match the resource we are working on
if len(filteredMatches) == 0 || res != filteredMatches[0] {
continue
}
}
// In the resource, note that it is referenced
// by the referrer.
res.AppendRefBy(f.Referrer.CurId())
// Return transformed name of the object,
// complete with prefixes, hashes, etc.
return res, nil
}
return oldName, "", nil
}
// utility function to replace a simple string by the new name
func getSimpleNameField(
oldName string,
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource,
isRoleRef bool) (string, error) {
newName, _, err := selectReferral(oldName, referrer, target,
referralCandidates, referralCandidateSubset, isRoleRef)
return newName, err
return nil, nil
}
func getIds(rs []*resource.Resource) []string {
@@ -225,73 +267,3 @@ func getIds(rs []*resource.Resource) []string {
}
return result
}
// utility function to replace name field within a map RNode
// and leverage the namespace field.
func setNameAndNs(
in *yaml.RNode,
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap,
isRoleRef bool) error {
if in.YNode().Kind != yaml.MappingNode {
return fmt.Errorf("expect a mapping node")
}
// Get name field
nameNode, err := in.Pipe(yaml.FieldMatcher{
Name: "name",
})
if err != nil || nameNode == nil {
return fmt.Errorf("cannot find field 'name' in node")
}
// Get namespace field
namespaceNode, err := in.Pipe(yaml.FieldMatcher{
Name: "namespace",
})
if err != nil {
return fmt.Errorf("error when find field 'namespace'")
}
// check is namespace matched
// name will bot be updated if the namespace doesn't match
subset := referralCandidates.Resources()
if namespaceNode != nil {
namespace := namespaceNode.YNode().Value
bynamespace := referralCandidates.GroupedByOriginalNamespace()
if _, ok := bynamespace[namespace]; !ok {
return nil
}
subset = bynamespace[namespace]
}
oldName := nameNode.YNode().Value
newname, newnamespace, err := selectReferral(oldName, referrer, target,
referralCandidates, subset, isRoleRef)
if err != nil {
return err
}
if (newname == oldName) && (newnamespace == "") {
// no candidate found.
return nil
}
// set name
in.Pipe(yaml.FieldSetter{
Name: "name",
StringValue: newname,
})
if newnamespace != "" {
// We don't want value "" to replace value "default" since
// the empty string is handled as a wild card here not default namespace
// by kubernetes.
in.Pipe(yaml.FieldSetter{
Name: "namespace",
StringValue: newnamespace,
})
}
return nil
}

View File

@@ -50,8 +50,8 @@ ref:
name: newName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -90,8 +90,8 @@ seq:
- oldName2
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "seq"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "seq"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -128,8 +128,8 @@ map:
name: newName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "map"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -169,8 +169,8 @@ map:
namespace: oldNs
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "map"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -207,8 +207,8 @@ map:
name: null
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "map"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -277,8 +277,8 @@ metadata:
originalNames: []string{"oldName", "oldName"},
expected: "",
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -308,8 +308,8 @@ metadata:
originalNames: []string{"oldName", "oldName"},
expected: "",
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -395,8 +395,8 @@ ref:
name: newName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -438,8 +438,8 @@ ref:
name: newName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -481,8 +481,8 @@ ref:
name: newName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -517,8 +517,8 @@ metadata:
inputSuffix: "suffix",
expected: "",
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -553,8 +553,8 @@ metadata:
inputSuffix: "",
expected: "",
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -589,8 +589,8 @@ metadata:
inputSuffix: "suffix",
expected: "",
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -632,8 +632,8 @@ ref:
name: oldName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -675,8 +675,8 @@ ref:
name: oldName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
@@ -718,8 +718,8 @@ ref:
name: oldName
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",

View File

@@ -4,6 +4,7 @@
package patchstrategicmerge
import (
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
@@ -15,6 +16,7 @@ type Filter struct {
var _ kio.Filter = Filter{}
// Filter does a strategic merge patch, which can delete nodes.
func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var result []*yaml.RNode
for i := range nodes {
@@ -27,7 +29,9 @@ func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
if err != nil {
return nil, err
}
result = append(result, r)
if !konfig.FlagEnableKyamlDefaultValue || r != nil {
result = append(result, r)
}
}
return result, nil
}

View File

@@ -1,3 +1,3 @@
// Package refvar contains a kio.Filter implementation of the kustomize
// refvar transformer.
// refvar transformer (find and replace $(FOO) style variables in strings).
package refvar

View File

@@ -1,12 +1,12 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package expansion provides functions find and replace $(FOO) style variables in strings.
package expansion
package refvar
import (
"bytes"
"fmt"
"log"
"strings"
)
const (
@@ -17,38 +17,64 @@ const (
// syntaxWrap returns the input string wrapped by the expansion syntax.
func syntaxWrap(input string) string {
return string(operator) + string(referenceOpener) + input + string(referenceCloser)
var sb strings.Builder
sb.WriteByte(operator)
sb.WriteByte(referenceOpener)
sb.WriteString(input)
sb.WriteByte(referenceCloser)
return sb.String()
}
// MappingFuncFor returns a mapping function for use with Expand that
// implements the expansion semantics defined in the expansion spec; it
// returns the input string wrapped in the expansion syntax if no mapping
// for the input is found.
func MappingFuncFor(
counts map[string]int,
context ...map[string]interface{}) func(string) interface{} {
return func(input string) interface{} {
for _, vars := range context {
val, ok := vars[input]
if ok {
counts[input]++
switch typedV := val.(type) {
case string, int64, float64, bool:
return typedV
default:
return syntaxWrap(input)
}
// MappingFunc maps a string to anything.
type MappingFunc func(string) interface{}
// MakePrimitiveReplacer returns a MappingFunc that uses a map to do
// replacements, and a histogram to count map hits.
//
// Func behavior:
//
// If the input key is NOT found in the map, the key is wrapped up as
// as a variable declaration string and returned, e.g. key FOO becomes $(FOO).
// This string is presumably put back where it was found, and might get replaced
// later.
//
// If the key is found in the map, the value is returned if it is a primitive
// type (string, bool, number), and the hit is counted.
//
// If it's not a primitive type (e.g. a map, struct, func, etc.) then this
// function doesn't know what to do with it and it returns the key wrapped up
// again as if it had not been replaced. This should probably be an error.
func MakePrimitiveReplacer(
counts map[string]int, someMap map[string]interface{}) MappingFunc {
return func(key string) interface{} {
if value, ok := someMap[key]; ok {
switch typedV := value.(type) {
case string, int, int32, int64, float32, float64, bool:
counts[key]++
return typedV
default:
// If the value is some complicated type (e.g. a map or struct),
// this function doesn't know how to jam it into a string,
// so just pretend it was a cache miss.
// Likely this should be an error instead of a silent failure,
// since the programmer passed an impossible value.
log.Printf(
"MakePrimitiveReplacer: bad replacement type=%T val=%v",
typedV, typedV)
return syntaxWrap(key)
}
}
return syntaxWrap(input)
// If unable to return the mapped variable, return it
// as it was found, and a later mapping might be able to
// replace it.
return syntaxWrap(key)
}
}
// Expand replaces variable references in the input string according to
// the expansion spec using the given mapping function to resolve the
// values of variables.
func Expand(input string, mapping func(string) interface{}) interface{} {
var buf bytes.Buffer
// DoReplacements replaces variable references in the input string
// using the mapping function.
func DoReplacements(input string, mapping MappingFunc) interface{} {
var buf strings.Builder
checkpoint := 0
for cursor := 0; cursor < len(input); cursor++ {
if input[cursor] == operator && cursor+1 < len(input) {

View File

@@ -1,13 +1,14 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package expansion_test
package refvar_test
import (
"fmt"
"testing"
. "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
"github.com/stretchr/testify/assert"
. "sigs.k8s.io/kustomize/api/filters/refvar"
)
type expected struct {
@@ -15,6 +16,48 @@ type expected struct {
edited string
}
func TestPrimitiveReplacer(t *testing.T) {
varCounts := make(map[string]int)
f := MakePrimitiveReplacer(
varCounts,
map[string]interface{}{
"FOO": "bar",
"ZOO": "$(FOO)-1",
"BLU": "$(ZOO)-2",
"EIGHT": 8,
"PI": 3.14159,
"ZINT": "$(INT)",
"BOOL": "true",
"HUGENUMBER": int64(9223372036854775807),
"CRAZYMAP": map[string]int{"crazy": 200},
"ZBOOL": "$(BOOL)",
})
assert.Equal(t, "$()", f(""))
assert.Equal(t, "$( )", f(" "))
assert.Equal(t, "$(florida)", f("florida"))
assert.Equal(t, "$(0)", f("0"))
assert.Equal(t, "bar", f("FOO"))
assert.Equal(t, "bar", f("FOO"))
assert.Equal(t, "bar", f("FOO"))
assert.Equal(t, 8, f("EIGHT"))
assert.Equal(t, 8, f("EIGHT"))
assert.Equal(t, 3.14159, f("PI"))
assert.Equal(t, "true", f("BOOL"))
assert.Equal(t, int64(9223372036854775807), f("HUGENUMBER"))
assert.Equal(t, "$(FOO)-1", f("ZOO"))
assert.Equal(t, "$(CRAZYMAP)", f("CRAZYMAP"))
assert.Equal(t,
map[string]int{
"FOO": 3,
"EIGHT": 2,
"BOOL": 1,
"PI": 1,
"ZOO": 1,
"HUGENUMBER": 1,
},
varCounts)
}
func TestMapReference(t *testing.T) {
type env struct {
Name string
@@ -51,7 +94,7 @@ func TestMapReference(t *testing.T) {
},
}
declaredEnv := map[string]interface{}{
varMap := map[string]interface{}{
"FOO": "bar",
"ZOO": "$(FOO)-1",
"BLU": "$(ZOO)-2",
@@ -61,11 +104,11 @@ func TestMapReference(t *testing.T) {
"ZBOOL": "$(BOOL)",
}
counts := make(map[string]int)
mapping := MappingFuncFor(counts, declaredEnv)
varCounts := make(map[string]int)
for _, env := range envs {
declaredEnv[env.Name] = Expand(fmt.Sprintf("%v", env.Value), mapping)
varMap[env.Name] = DoReplacements(
fmt.Sprintf("%v", env.Value),
MakePrimitiveReplacer(varCounts, varMap))
}
expectedEnv := map[string]expected{
@@ -79,45 +122,20 @@ func TestMapReference(t *testing.T) {
}
for k, v := range expectedEnv {
if e, a := v, declaredEnv[k]; e.edited != a || e.count != counts[k] {
if e, a := v, varMap[k]; e.edited != a || e.count != varCounts[k] {
t.Errorf("Expected %v count=%d, got %v count=%d",
e.edited, e.count, a, counts[k])
e.edited, e.count, a, varCounts[k])
} else {
delete(declaredEnv, k)
delete(varMap, k)
}
}
if len(declaredEnv) != 0 {
t.Errorf("Unexpected keys in declared env: %v", declaredEnv)
if len(varMap) != 0 {
t.Errorf("Unexpected keys in declared env: %v", varMap)
}
}
func TestMapping(t *testing.T) {
context := map[string]interface{}{
"VAR_A": "A",
"VAR_B": "B",
"VAR_C": "C",
"VAR_REF": "$(VAR_A)",
"VAR_EMPTY": "",
}
doExpansionTest(t, context)
}
func TestMappingDual(t *testing.T) {
context := map[string]interface{}{
"VAR_A": "A",
"VAR_EMPTY": "",
}
context2 := map[string]interface{}{
"VAR_B": "B",
"VAR_C": "C",
"VAR_REF": "$(VAR_A)",
}
doExpansionTest(t, context, context2)
}
func doExpansionTest(t *testing.T, context ...map[string]interface{}) {
cases := []struct {
name string
input string
@@ -333,11 +351,17 @@ func doExpansionTest(t *testing.T, context ...map[string]interface{}) {
expected: "\n",
},
}
for _, tc := range cases {
counts := make(map[string]int)
mapping := MappingFuncFor(counts, context...)
expanded := Expand(fmt.Sprintf("%v", tc.input), mapping)
expanded := DoReplacements(
fmt.Sprintf("%v", tc.input),
MakePrimitiveReplacer(counts, map[string]interface{}{
"VAR_A": "A",
"VAR_B": "B",
"VAR_C": "C",
"VAR_REF": "$(VAR_A)",
"VAR_EMPTY": "",
}))
if e, a := tc.expected, expanded; e != a {
t.Errorf("%v: expected %q, got %q", tc.name, e, a)
}
@@ -347,8 +371,7 @@ func doExpansionTest(t *testing.T, context ...map[string]interface{}) {
}
if len(tc.counts) > 0 {
for k, expectedCount := range tc.counts {
c, ok := counts[k]
if ok {
if c, ok := counts[k]; ok {
if c != expectedCount {
t.Errorf(
"%v: k=%s, expected count %d, got %d",

View File

@@ -8,15 +8,13 @@ import (
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
)
// Filter updates $(VAR) style variables with values.
// The fieldSpecs are the places to look for occurrences of $(VAR).
type Filter struct {
MappingFunc func(string) interface{} `json:"mappingFunc,omitempty" yaml:"mappingFunc,omitempty"`
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
MappingFunc MappingFunc `json:"mappingFunc,omitempty" yaml:"mappingFunc,omitempty"`
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
}
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
@@ -49,12 +47,21 @@ func (f Filter) set(node *yaml.RNode) error {
func updateNodeValue(node *yaml.Node, newValue interface{}) {
switch newValue := newValue.(type) {
case int:
node.Value = strconv.FormatInt(int64(newValue), 10)
node.Tag = yaml.NodeTagInt
case int32:
node.Value = strconv.FormatInt(int64(newValue), 10)
node.Tag = yaml.NodeTagInt
case int64:
node.Value = strconv.FormatInt(newValue, 10)
node.Tag = yaml.NodeTagInt
case bool:
node.SetString(strconv.FormatBool(newValue))
node.Tag = yaml.NodeTagBool
case float32:
node.SetString(strconv.FormatFloat(float64(newValue), 'f', -1, 32))
node.Tag = yaml.NodeTagFloat
case float64:
node.SetString(strconv.FormatFloat(newValue, 'f', -1, 64))
node.Tag = yaml.NodeTagFloat
@@ -69,7 +76,7 @@ func (f Filter) setScalar(node *yaml.RNode) error {
if !yaml.IsYNodeString(node.YNode()) {
return nil
}
v := expansion2.Expand(node.YNode().Value, f.MappingFunc)
v := DoReplacements(node.YNode().Value, f.MappingFunc)
updateNodeValue(node.YNode(), v)
return nil
}
@@ -78,12 +85,14 @@ func (f Filter) setMap(node *yaml.RNode) error {
contents := node.YNode().Content
for i := 0; i < len(contents); i += 2 {
if !yaml.IsYNodeString(contents[i]) {
return fmt.Errorf("invalid map key: %s, type: %s", contents[i].Value, contents[i].Tag)
return fmt.Errorf(
"invalid map key: value='%s', tag='%s'",
contents[i].Value, contents[i].Tag)
}
if !yaml.IsYNodeString(contents[i+1]) {
continue
}
newValue := expansion2.Expand(contents[i+1].Value, f.MappingFunc)
newValue := DoReplacements(contents[i+1].Value, f.MappingFunc)
updateNodeValue(contents[i+1], newValue)
}
return nil
@@ -94,7 +103,7 @@ func (f Filter) setSeq(node *yaml.RNode) error {
if !yaml.IsYNodeString(item) {
return fmt.Errorf("invalid value type expect a string")
}
newValue := expansion2.Expand(item.Value, f.MappingFunc)
newValue := DoReplacements(item.Value, f.MappingFunc)
updateNodeValue(item, newValue)
}
return nil

View File

@@ -1,18 +1,22 @@
package refvar
package refvar_test
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
. "sigs.k8s.io/kustomize/api/filters/refvar"
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
var makeMf = func(theMap map[string]interface{}) MappingFunc {
ignored := make(map[string]int)
return MakePrimitiveReplacer(ignored, theMap)
}
func TestFilter(t *testing.T) {
replacementCounts := make(map[string]int)
testCases := map[string]struct {
input string
@@ -35,7 +39,7 @@ metadata:
spec:
replicas: 5`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5),
}),
FieldSpec: types.FieldSpec{Path: "spec/replicas"},
@@ -57,7 +61,7 @@ metadata:
spec:
replicas: 1`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5),
}),
FieldSpec: types.FieldSpec{Path: "spec/replicas"},
@@ -79,7 +83,7 @@ metadata:
spec:
replicas: 1`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5),
}),
FieldSpec: types.FieldSpec{Path: "a/b/c"},
@@ -111,7 +115,7 @@ data:
- false
- 1.23`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"FOO": "foo",
"BAR": "bar",
"BOOL": false,
@@ -142,7 +146,7 @@ data:
BAZ: $(BAZ)
PLUS: foo+bar`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"FOO": "foo",
"BAR": "bar",
}),
@@ -181,7 +185,7 @@ data:
SLICE:
- $(FOO)`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"FOO": "foo",
"BAR": "bar",
}),
@@ -204,8 +208,10 @@ metadata:
data:
FOO: null`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{}),
FieldSpec: types.FieldSpec{Path: "data/FOO"},
MappingFunc: makeMf(map[string]interface{}{
// no replacements!
}),
FieldSpec: types.FieldSpec{Path: "data/FOO"},
},
},
}
@@ -223,8 +229,6 @@ data:
}
func TestFilterUnhappy(t *testing.T) {
replacementCounts := make(map[string]int)
testCases := map[string]struct {
input string
expectedError string
@@ -250,7 +254,7 @@ data:
- false
' at path 'data/slice': invalid value type expect a string`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5),
}),
FieldSpec: types.FieldSpec{Path: "data/slice"},
@@ -272,9 +276,9 @@ metadata:
config.kubernetes.io/index: '0'
data:
1: str
' at path 'data': invalid map key: 1, type: ` + yaml.NodeTagInt,
' at path 'data': invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{
MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5),
}),
FieldSpec: types.FieldSpec{Path: "data"},

View File

@@ -16,13 +16,11 @@ require (
github.com/yujunz/go-getter v1.5.1-lite.0.20201201013212-6d9c071adddf
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
sigs.k8s.io/kustomize/kyaml v0.10.3
sigs.k8s.io/kustomize/kyaml v0.10.6
sigs.k8s.io/yaml v1.2.0
)
replace sigs.k8s.io/kustomize/kyaml => ../kyaml

View File

@@ -572,8 +572,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-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
@@ -599,6 +599,8 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/kyaml v0.10.6 h1:xUJxc/k8JoWqHUahaB8DTqY0KwEPxTbTGStvW8TOcDc=
sigs.k8s.io/kustomize/kyaml v0.10.6/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

View File

@@ -9,7 +9,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/nameref"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
)
type nameReferenceTransformer struct {
@@ -20,7 +19,8 @@ var _ resmap.Transformer = &nameReferenceTransformer{}
// newNameReferenceTransformer constructs a nameReferenceTransformer
// with a given slice of NameBackReferences.
func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.Transformer {
func newNameReferenceTransformer(
br []builtinconfig.NameBackReferences) resmap.Transformer {
if br == nil {
log.Fatal("backrefs not expected to be nil")
}
@@ -29,17 +29,17 @@ func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.T
// Transform updates name references in resource A that
// refer to resource B, given that B's name may have
// changed.
// changed. A is the referrer, B is the referralTarget.
//
// For example, a HorizontalPodAutoscaler (HPA)
// necessarily refers to a Deployment, the thing that
// the HPA scales. The Deployment name might change
// the HPA scales. The Deployment's name might change
// (e.g. prefix added), and the reference in the HPA
// has to be fixed.
//
// In the outer loop over the ResMap below, say we
// encounter a specific HPA. Then, in scanning backrefs,
// we encounter an entry like
// encounter a specific HPA. Then, in scanning the set
// of all known backrefs, we encounter an entry like
//
// - kind: Deployment
// fieldSpecs:
@@ -74,22 +74,32 @@ func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.T
// Name transformers should only modify the name in the
// body of the resource object (the value in the ResMap).
//
func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
func (t *nameReferenceTransformer) Transform(m resmap.ResMap) error {
// TODO: Too much looping, here and in transitive calls.
for _, referrer := range m.Resources() {
var candidates resmap.ResMap
for _, target := range o.backRefs {
for _, fSpec := range target.FieldSpecs {
for _, referralTarget := range t.backRefs {
for _, fSpec := range referralTarget.FieldSpecs {
if referrer.OrgId().IsSelected(&fSpec.Gvk) {
if candidates == nil {
// This excludes objects from other namespaces.
// In most realistic uses, it returns all elements of m,
// (since they're all in the same namespace).
candidates = m.SubsetThatCouldBeReferencedByResource(referrer)
}
err := filtersutil.ApplyToJSON(nameref.Filter{
FieldSpec: fSpec,
// One way to get here is with, say, a referrer that's an
// HPA, and a target that's a Deployment (one of the
// Deployment's fieldSpecs selects an HPA). Now we look
// through the candidates to see if one is a Deployment
// (the target), and if so, get the Deployment's name and
// write it into the referrer, at the field specfied in
// fSpec.
err := referrer.ApplyFilter(nameref.Filter{
Referrer: referrer,
Target: target.Gvk,
NameFieldToUpdate: fSpec,
ReferralTarget: referralTarget.Gvk,
ReferralCandidates: candidates,
}, referrer)
})
if err != nil {
return err
}

View File

@@ -721,6 +721,7 @@ func TestNameReferenceNamespace(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
@@ -882,6 +883,7 @@ func TestNameReferenceClusterWide(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
@@ -1008,6 +1010,7 @@ func TestNameReferenceNamespaceTransformation(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
@@ -1044,6 +1047,7 @@ func TestNameReferenceCandidateSelection(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}

View File

@@ -4,19 +4,15 @@
package accumulator
import (
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
"sigs.k8s.io/kustomize/api/filters/refvar"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
)
type refVarTransformer struct {
varMap map[string]interface{}
replacementCounts map[string]int
fieldSpecs []types.FieldSpec
mappingFunc func(string) interface{}
}
// newRefVarTransformer returns a new refVarTransformer
@@ -35,8 +31,7 @@ func newRefVarTransformer(
func (rv *refVarTransformer) UnusedVars() []string {
var unused []string
for k := range rv.varMap {
_, ok := rv.replacementCounts[k]
if !ok {
if _, ok := rv.replacementCounts[k]; !ok {
unused = append(unused, k)
}
}
@@ -46,14 +41,13 @@ func (rv *refVarTransformer) UnusedVars() []string {
// Transform replaces $(VAR) style variables with values.
func (rv *refVarTransformer) Transform(m resmap.ResMap) error {
rv.replacementCounts = make(map[string]int)
rv.mappingFunc = expansion2.MappingFuncFor(
rv.replacementCounts, rv.varMap)
mf := refvar.MakePrimitiveReplacer(rv.replacementCounts, rv.varMap)
for _, res := range m.Resources() {
for _, fieldSpec := range rv.fieldSpecs {
err := filtersutil.ApplyToJSON(refvar.Filter{
MappingFunc: rv.mappingFunc,
err := res.ApplyFilter(refvar.Filter{
MappingFunc: mf,
FieldSpec: fieldSpec,
}, res)
})
if err != nil {
return err
}

View File

@@ -7,6 +7,7 @@ import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
@@ -123,8 +124,19 @@ func TestRefVarTransformer(t *testing.T) {
"slice": []interface{}{5}, // noticeably *not* a []string
}}).ResMap(),
},
errMessage: `obj '{"apiVersion": "v1", "data": {"slice": [5]}, "kind": "ConfigMap", "metadata": {"name": "cm1"}}
// TODO(#3304): DECISION - kyaml better; not a bug.
errMessage: konfig.IfApiMachineryElseKyaml(
`obj '{"apiVersion": "v1", "data": {"slice": [5]}, "kind": "ConfigMap", "metadata": {"name": "cm1"}}
' at path 'data/slice': invalid value type expect a string`,
`obj 'apiVersion: v1
data:
slice:
- 5
kind: ConfigMap
metadata:
name: cm1
' at path 'data/slice': invalid value type expect a string`,
),
},
{
description: "var replacement in nil",

View File

@@ -59,6 +59,15 @@ func (ra *ResAccumulator) GetTransformerConfig() *builtinconfig.TransformerConfi
return ra.tConfig
}
// MergeVars accumulates vars into ResAccumulator.
// A Var is a tuple of name, object reference and field reference.
// This func takes a list of vars from the current kustomization file and
// annotates the accumulated resources with the names of the vars that match
// those resources. E.g. if there's a var named "sam" that wants to get
// its data from a ConfigMap named "james", and the resource list contains a
// ConfigMap named "james", then that ConfigMap will be annotated with the
// var name "sam". Later this annotation is used to find the data for "sam"
// by digging into a particular fieldpath of "james".
func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
for _, v := range incoming {
targetId := resid.NewResIdWithNamespace(v.ObjRef.GVK(), v.ObjRef.Name, v.ObjRef.Namespace)
@@ -104,12 +113,10 @@ func (ra *ResAccumulator) findVarValueFromResources(v types.Var) (interface{}, e
"field specified in var '%v' "+
"not found in corresponding resource", v)
}
return s, nil
}
}
}
return "", fmt.Errorf(
"var '%v' cannot be mapped to a field "+
"in the set of known resources", v)
@@ -125,10 +132,8 @@ func (ra *ResAccumulator) makeVarReplacementMap() (map[string]interface{}, error
if err != nil {
return nil, err
}
result[v.Name] = s
}
return result, nil
}
@@ -159,6 +164,6 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
if ra.tConfig.NameReference == nil {
return nil
}
return ra.Transform(newNameReferenceTransformer(
ra.tConfig.NameReference))
return ra.Transform(
newNameReferenceTransformer(ra.tConfig.NameReference))
}

View File

@@ -355,7 +355,8 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
// went through a prefix transformer.
r := m.GetByIndex(1)
r.AddNamePrefix("sub-")
r.SetName("sub-backendOne") // original name remains "backendOne"
r.SetName("sub-backendOne")
r.SetOriginalName("backendOne", true)
err = ra2.AppendAll(m)
if err != nil {

View File

@@ -512,8 +512,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-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
@@ -534,8 +534,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.10.3 h1:ARSJUMN/c3k31DYxRfZ+vp/UepUQjg9zCwny7Oj908I=
sigs.k8s.io/kustomize/kyaml v0.10.3/go.mod h1:RA+iCHA2wPCOfv6uG6TfXXWhYsHpgErq/AljxWKuxtg=
sigs.k8s.io/kustomize/kyaml v0.10.6 h1:xUJxc/k8JoWqHUahaB8DTqY0KwEPxTbTGStvW8TOcDc=
sigs.k8s.io/kustomize/kyaml v0.10.6/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
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

@@ -4,7 +4,6 @@
package generators
import (
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -38,14 +37,9 @@ func MakeConfigMap(
if err != nil {
return nil, err
}
for _, k := range filtersutil.SortedMapKeys(m) {
fldName, vrN := makeConfigMapValueRNode(m[k])
if _, err = rn.Pipe(
yaml.LookupCreate(yaml.MappingNode, fldName),
yaml.SetField(k, vrN)); err != nil {
return nil, err
}
if err = rn.LoadMapIntoConfigMapData(m); err != nil {
return nil, err
}
copyLabelsAndAnnotations(rn, args.Options)
return rn, err
return rn, nil
}

View File

@@ -4,7 +4,6 @@
package generators
import (
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -51,14 +50,9 @@ func MakeSecret(
if err != nil {
return nil, err
}
for _, k := range filtersutil.SortedMapKeys(m) {
vrN := makeSecretValueRNode(m[k])
if _, err = rn.Pipe(
yaml.LookupCreate(yaml.MappingNode, yaml.DataField),
yaml.SetField(k, vrN)); err != nil {
return nil, err
}
if err = rn.LoadMapIntoSecretData(m); err != nil {
return nil, err
}
copyLabelsAndAnnotations(rn, args.Options)
return rn, err
return rn, nil
}

View File

@@ -4,13 +4,9 @@
package generators
import (
"encoding/base64"
"fmt"
"strings"
"unicode/utf8"
"github.com/go-errors/errors"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -66,13 +62,13 @@ func copyLabelsAndAnnotations(
if opts == nil {
return nil
}
for _, k := range filtersutil.SortedMapKeys(opts.Labels) {
for _, k := range yaml.SortedMapKeys(opts.Labels) {
v := opts.Labels[k]
if _, err := rn.Pipe(yaml.SetLabel(k, v)); err != nil {
return err
}
}
for _, k := range filtersutil.SortedMapKeys(opts.Annotations) {
for _, k := range yaml.SortedMapKeys(opts.Annotations) {
v := opts.Annotations[k]
if _, err := rn.Pipe(yaml.SetAnnotation(k, v)); err != nil {
return err
@@ -80,60 +76,3 @@ func copyLabelsAndAnnotations(
}
return nil
}
// In a secret, all data is base64 encoded, regardless of its conformance
// or lack thereof to UTF-8.
func makeSecretValueRNode(s string) *yaml.RNode {
yN := &yaml.Node{Kind: yaml.ScalarNode}
// Purposely don't use YAML tags to identify the data as being plain text or
// binary. It kubernetes Secrets the values in the `data` map are expected
// to be base64 encoded, and in ConfigMaps that same can be said for the
// values in the `binaryData` field.
yN.Tag = yaml.NodeTagString
yN.Value = encodeBase64(s)
if strings.Contains(yN.Value, "\n") {
yN.Style = yaml.LiteralStyle
}
return yaml.NewRNode(yN)
}
func makeConfigMapValueRNode(s string) (field string, rN *yaml.RNode) {
yN := &yaml.Node{Kind: yaml.ScalarNode}
yN.Tag = yaml.NodeTagString
if utf8.ValidString(s) {
field = yaml.DataField
yN.Value = s
} else {
field = yaml.BinaryDataField
yN.Value = encodeBase64(s)
}
if strings.Contains(yN.Value, "\n") {
yN.Style = yaml.LiteralStyle
}
return field, yaml.NewRNode(yN)
}
// encodeBase64 encodes s as base64 that is broken up into multiple lines
// as appropriate for the resulting length.
func encodeBase64(s string) string {
const lineLen = 70
encLen := base64.StdEncoding.EncodedLen(len(s))
lines := encLen/lineLen + 1
buf := make([]byte, encLen*2+lines)
in := buf[0:encLen]
out := buf[encLen:]
base64.StdEncoding.Encode(in, []byte(s))
k := 0
for i := 0; i < len(in); i += lineLen {
j := i + lineLen
if j > len(in) {
j = len(in)
}
k += copy(out[k:], in[i:j])
if lines > 1 {
out[k] = '\n'
k++
}
}
return string(out[:k])
}

View File

@@ -45,6 +45,7 @@ s/$BAR/bar baz/g
"argsFromFile": "sed-input.txt",
})
pluginConfig.RemoveBuildAnnotations()
p := NewExecPlugin(
pLdr.AbsolutePluginPath(
konfig.DisabledPluginConfig(),

View File

@@ -244,6 +244,7 @@ metadata:
t.Fatalf("unexpected error %v", err)
}
}
expected.RemoveBuildAnnotations()
expYaml, err := expected.AsYaml()
assert.NoError(t, err)
@@ -251,6 +252,7 @@ metadata:
assert.NoError(t, kt.Load())
actual, err := kt.MakeCustomizedResMap()
assert.NoError(t, err)
actual.RemoveBuildAnnotations()
actYaml, err := actual.AsYaml()
assert.NoError(t, err)
assert.Equal(t, expYaml, actYaml)

View File

@@ -58,7 +58,7 @@ func (k *WNodeFactory) SliceFromBytes(bs []byte) ([]ifc.Kunstructured, error) {
// shouldDropObject returns true if the resource should not be accumulated.
func shouldDropObject(m yaml.ResourceMeta) bool {
_, y := m.ObjectMeta.Annotations[konfig.IgnoredByKustomizeResourceAnnotation]
_, y := m.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
return y
}

View File

@@ -6,6 +6,8 @@ package wrappy
import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
"sigs.k8s.io/kustomize/api/ifc"
@@ -43,6 +45,10 @@ func FromRNode(node *yaml.RNode) *WNode {
return &WNode{node: node}
}
func (wn *WNode) AsRNode() *yaml.RNode {
return wn.node
}
func (wn *WNode) demandMetaData(label string) yaml.ResourceMeta {
meta, err := wn.node.GetMeta()
if err != nil {
@@ -62,9 +68,35 @@ func (wn *WNode) GetAnnotations() map[string]string {
return wn.demandMetaData("GetAnnotations").Annotations
}
// convertSliceIndex traverses the items in `fields` and find
// if there is a slice index in the item and change it to a
// valid Lookup field path. For example, 'ports[0]' will be
// converted to 'ports' and '0'.
func convertSliceIndex(fields []string) []string {
var res []string
for _, s := range fields {
if !strings.HasSuffix(s, "]") {
res = append(res, s)
continue
}
re := regexp.MustCompile(`^(.*)\[(\d+)\]$`)
groups := re.FindStringSubmatch(s)
if len(groups) == 0 {
// no match, add to result
res = append(res, s)
continue
}
if groups[1] != "" {
res = append(res, groups[1])
}
res = append(res, groups[2])
}
return res
}
// GetFieldValue implements ifc.Kunstructured.
func (wn *WNode) GetFieldValue(path string) (interface{}, error) {
fields := strings.Split(path, ".")
fields := convertSliceIndex(strings.Split(path, "."))
rn, err := wn.node.Pipe(yaml.Lookup(fields...))
if err != nil {
return nil, err
@@ -96,9 +128,29 @@ func (wn *WNode) GetFieldValue(path string) (interface{}, error) {
}
return result, nil
}
if yn.Kind != yaml.ScalarNode {
return nil, fmt.Errorf("expected ScalarNode, got Kind=%d", yn.Kind)
}
// Return value value directly for all other (ScalarNode) kinds
return yn.Value, nil
// TODO: When doing kustomize var replacement, which is likely a
// a primary use of this function and the reason it returns interface{}
// rather than string, we do conversion from Nodes to Go types and back
// to nodes. We should figure out how to do replacement using raw nodes,
// assuming we keep the var feature in kustomize.
// The other end of this is: refvar.go:updateNodeValue.
switch yn.Tag {
case yaml.NodeTagString:
return yn.Value, nil
case yaml.NodeTagInt:
return strconv.Atoi(yn.Value)
case yaml.NodeTagFloat:
return strconv.ParseFloat(yn.Value, 64)
case yaml.NodeTagBool:
return strconv.ParseBool(yn.Value)
default:
// Possibly this should be an error or log.
return yn.Value, nil
}
}
// GetGvk implements ifc.Kunstructured.
@@ -159,12 +211,7 @@ func (wn *WNode) GetString(path string) (string, error) {
// Map implements ifc.Kunstructured.
func (wn *WNode) Map() map[string]interface{} {
var result map[string]interface{}
if err := wn.node.YNode().Decode(&result); err != nil {
// Log and die since interface doesn't allow error.
log.Fatalf("failed to decode ynode: %v", err)
}
return result
return wn.node.Map()
}
// MarshalJSON implements ifc.Kunstructured.

View File

@@ -8,6 +8,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"sigs.k8s.io/kustomize/api/resid"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -480,6 +481,10 @@ func TestGetSlice(t *testing.T) {
}
}
func TestMapEmpty(t *testing.T) {
assert.Equal(t, 0, len(NewWNode().Map()))
}
func TestMap(t *testing.T) {
wn := NewWNode()
if err := wn.UnmarshalJSON([]byte(deploymentLittleJson)); err != nil {

View File

@@ -125,7 +125,7 @@ func (kf *KunstructuredFactoryImpl) skipResource(u unstructured.Unstructured) bo
return false
}
// check if the Resource has opt-ed out of kustomize
_, found := an[konfig.IgnoredByKustomizeResourceAnnotation]
_, found := an[konfig.IgnoredByKustomizeAnnotation]
return found
}

View File

@@ -43,7 +43,7 @@ const (
// Historically, tests passed for enable_kyaml == false, i.e. using
// apimachinery libs. This doesn't mean the code was better, it just
// means regression tests preserved those outcomes.
FlagEnableKyamlDefaultValue = false
FlagEnableKyamlDefaultValue = true
// An environment variable to consult for kustomization
// configuration data. See:
@@ -56,8 +56,11 @@ const (
// A program name, for use in help, finding the XDG_CONFIG_DIR, etc.
ProgramName = "kustomize"
// ConfigAnnoDomain is configuration-related annotation namespace.
ConfigAnnoDomain = "config.kubernetes.io"
// If a resource has this annotation, kustomize will drop it.
IgnoredByKustomizeResourceAnnotation = "config.kubernetes.io/local-config"
IgnoredByKustomizeAnnotation = ConfigAnnoDomain + "/local-config"
// Label key that indicates the resources are built from Kustomize
ManagedbyLabelKey = "app.kubernetes.io/managed-by"

View File

@@ -9,7 +9,6 @@ import (
"runtime"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/types"
)
@@ -158,15 +157,3 @@ func pwdEnv() string {
}
return "PWD"
}
// GetBuiltinPluginNames returns a list of builtin plugin names
func GetBuiltinPluginNames() []string {
var ret []string
for k := range builtinhelpers.GeneratorFactories {
ret = append(ret, k.String())
}
for k := range builtinhelpers.TransformerFactories {
ret = append(ret, k.String())
}
return ret
}

102
api/krusty/basic_io_test.go Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestBasicIO_1(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
if !opts.UseKyaml {
// This test won't pass under apimachinery, because in the bowels of
// that code (see GetAnnotations in v0.17.0 of
// k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go)
// an error returned from NestedStringMap is discarded, and an
// empty annotation map is silently returned, making this test fail
// The swallowed error arises from code like:
// var v interface{}
// v = true
// if str, ok := v.(string); ok {
// save the value in a map (doesn't happen)
// } else {
// return an error (that is then ignored by GetAnnotations)
// }
// The error happens when any annotation value can be interpreted as
// a boolean or number. Such annotations cannot be successfully applied
// to an object in a cluster unless they are quoted.
t.SkipNow()
}
th.WriteK(".", `
resources:
- service.yaml
`)
th.WriteF("service.yaml", `
apiVersion: v1
kind: Service
metadata:
annotations:
port: 8080
happy: true
color: green
name: demo
spec:
clusterIP: None
`)
m := th.Run(".", opts)
// The annotations are sorted by key, hence the order change.
// Quotes are added intentionally.
th.AssertActualEqualsExpected(
m, `
apiVersion: v1
kind: Service
metadata:
annotations:
color: green
happy: "true"
port: "8080"
name: demo
spec:
clusterIP: None
`)
}
func TestBasicIO_2(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
th.WriteK(".", `
resources:
- service.yaml
`)
// All the annotation values are quoted in the input.
th.WriteF("service.yaml", `
apiVersion: v1
kind: Service
metadata:
annotations:
port: "8080"
happy: "true"
color: green
name: demo
spec:
clusterIP: None
`)
m := th.Run(".", opts)
// The annotations are sorted by key, hence the order change.
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Service
metadata:
annotations:
color: green
happy: "true"
port: "8080"
name: demo
spec:
clusterIP: None
`)
}

View File

@@ -27,10 +27,6 @@ configMapGenerator:
apiVersion: v1
kind: Service
metadata:
annotations:
port: 8080
happy: true
color: green
name: demo
spec:
clusterIP: None
@@ -41,10 +37,6 @@ spec:
apiVersion: v1
kind: Service
metadata:
annotations:
color: green
happy: true
port: 8080
name: demo
spec:
clusterIP: None
@@ -60,7 +52,6 @@ metadata:
`)
}
// Observation: Numbers no longer quoted
func TestGeneratorIntVsStringWithMerge(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
@@ -80,22 +71,52 @@ configMapGenerator:
literals:
- month=12
`)
opts := th.MakeDefaultOptions()
m := th.Run("overlay", opts)
expFmt := `apiVersion: v1
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: v1
data:
crisis: %s
crisis: "true"
fruit: Indian Gooseberry
month: %s
year: %s
month: "12"
year: "2020"
kind: ConfigMap
metadata:
name: bob-%s
`
th.AssertActualEqualsExpected(
m, opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, `"true"`, `"12"`, `"2020"`, `bk46gm59c6`),
fmt.Sprintf(expFmt, `true`, `12`, `2020`, `bkmtk2t2fb`)))
name: bob-bk46gm59c6
`)
}
func TestGeneratorFromProperties(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
configMapGenerator:
- name: test-configmap
behavior: create
envs:
- properties
`)
th.WriteF("base/properties", `
VAR1=100
`)
th.WriteK("overlay", `
resources:
- ../base
configMapGenerator:
- name: test-configmap
behavior: "merge"
envs:
- properties
`)
th.WriteF("overlay/properties", `
VAR2=200
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: v1
data:
VAR1: "100"
VAR2: "200"
kind: ConfigMap
metadata:
name: test-configmap-hdghb5ddkg
`)
}
// Generate a Secret and a ConfigMap from the same data
@@ -193,12 +214,16 @@ metadata:
type: Opaque
`
th.AssertActualEqualsExpected(
m, opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt,
m,
// TODO(#3304): DECISION - kyaml better; not a bug.
opts.IfApiMachineryElseKyaml(
fmt.Sprintf(
expFmt,
`CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbnVjbGVhcgo=`,
`CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aGUgZXZpbCBkYXlzIGNvbWUgbm90Lgo=`,
`ftht6hfgmb`),
fmt.Sprintf(expFmt, `|
fmt.Sprintf(
expFmt, `|
CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbn
VjbGVhcgo=`, `|
CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aG
@@ -440,3 +465,40 @@ metadata:
name: cm-o2-5k95kd76ft
`)
}
func TestConfigMapGeneratorLiteralNewline(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
generators:
- configmaps.yaml
`)
th.WriteF("/app/configmaps.yaml", `
apiVersion: builtin
kind: ConfigMapGenerator
metadata:
name: testing
literals:
- |
initial.txt=greetings
everyone
- |
final.txt=different
behavior
---
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(
m, `
apiVersion: v1
data:
final.txt: |
different
behavior
initial.txt: |
greetings
everyone
kind: ConfigMap
metadata:
name: testing-tt4769fb52
`)
}

View File

@@ -1,7 +1,6 @@
package krusty_test
import (
"fmt"
"os/exec"
"testing"
@@ -129,9 +128,8 @@ stringData:
bootcmd:
- mkdir /mnt/vda
`)
opts := th.MakeOptionsPluginsEnabled()
m := th.Run("/app", opts)
expFmt := `
m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Secret
metadata:
@@ -154,7 +152,7 @@ metadata:
name: demo
name: demo-budget
spec:
minAvailable: 67%%
minAvailable: 67%
selector:
matchLabels:
app: cockroachdb
@@ -187,7 +185,9 @@ metadata:
annotations:
config.kubernetes.io/path: config/demo_service.yaml
prometheus.io/path: _status/vars
%s
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
labels:
app: cockroachdb
name: demo
@@ -244,7 +244,7 @@ spec:
- /bin/bash
- -ecx
- |
# The use of qualified ` + "`hostname -f`" + ` is crucial:
# The use of qualified `+"`hostname -f`"+` is crucial:
# Other nodes aren't able to look up the unqualified hostname.
CRARGS=("start" "--logtostderr" "--insecure" "--host" "$(hostname -f)" "--http-host" "0.0.0.0")
# We only want to initialize a new cluster (by omitting the join flag)
@@ -302,14 +302,7 @@ spec:
resources:
requests:
storage: 1Gi
`
th.AssertActualEqualsExpected(m, opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, ` prometheus.io/port: "8080"
prometheus.io/scrape: "true"
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"`),
fmt.Sprintf(expFmt, ` prometheus.io/port: 8080
prometheus.io/scrape: true
service.alpha.kubernetes.io/tolerate-unready-endpoints: true`)))
`)
}
func TestFnContainerTransformer(t *testing.T) {

View File

@@ -93,5 +93,6 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
}
t.Transform(m)
}
m.RemoveBuildAnnotations()
return m, nil
}

View File

@@ -318,8 +318,9 @@ spec:
volumes:%s
name: nginx-persistent-storage
`
// TODO(#3394)
th.AssertActualEqualsExpected(
// TODO(#3394): Should be possible to delete emptyDir with a patch.
// TODO(#3304): DECISION - still a bug, emptyDir should be deleted.
m, opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, `
- gcePersistentDisk:

View File

@@ -532,9 +532,6 @@ vars:
func TestVariablesAmbiguousWorkaround(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
if opts.UseKyaml {
t.Skip("TODO(#3396)")
}
th.WriteK("dev", namespaceNeedInVarDevFolder)
th.WriteF("dev/elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources)
th.WriteK("test", namespaceNeedInVarTestFolder)
@@ -591,13 +588,68 @@ vars:
// to the variable declarations allows to disambiguate the variables.
func TestVariablesDisambiguatedWithNamespace(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
if opts.UseKyaml {
t.Skip("TODO(#3396)")
}
th.WriteK(".", namespaceNeedInVarMyAppWithNamespace)
th.WriteF("elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources)
th.WriteF("elasticsearch-test-service.yaml", namespaceNeedInVarTestResources)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, namespaceNeedInVarExpectedOutput)
}
// TestAddNamePrefixWithNamespace tests that adding a name prefix works within
// namespaces other than the default namespace.
// Test for issue #3430
func TestAddNamePrefixWithNamespace(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("/app/serviceaccount.yaml", `
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
`)
th.WriteF("/app/clusterrolebinding.yaml", `
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: iter8-monitoring
`)
th.WriteK("/app", `
namePrefix: iter8-
namespace: iter8-monitoring
resources:
- clusterrolebinding.yaml
- serviceaccount.yaml
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: iter8-prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: iter8-prometheus
namespace: iter8-monitoring
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: iter8-prometheus
namespace: iter8-monitoring
`)
}

View File

@@ -4,6 +4,7 @@
package krusty
import (
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
)
@@ -61,3 +62,15 @@ func (o Options) IfApiMachineryElseKyaml(s1, s2 string) string {
}
return s2
}
// GetBuiltinPluginNames returns a list of builtin plugin names
func GetBuiltinPluginNames() []string {
var ret []string
for k := range builtinhelpers.GeneratorFactories {
ret = append(ret, k.String())
}
for k := range builtinhelpers.TransformerFactories {
ret = append(ret, k.String())
}
return ret
}

View File

@@ -0,0 +1,96 @@
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestPatchDeleteOfNotExistingAttributesShouldNotAddExtraElements(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("resource.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatever
spec:
template:
spec:
containers:
- env:
- name: EXISTING
value: EXISTING_VALUE
- name: FOR_REMOVAL
value: FOR_REMOVAL_VALUE
name: whatever
image: helloworld
`)
th.WriteF("patch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatever
spec:
template:
spec:
containers:
- name: whatever
env:
- name: FOR_REMOVAL
$patch: delete
- name: NOT_EXISTING_FOR_REMOVAL
$patch: delete
`)
th.WriteK(".", `
resources:
- resource.yaml
patches:
- path: patch.yaml
target:
kind: Deployment
`)
// It's expected that removal of not existing elements should not introduce extra values,
// as a patch can be applied to multiple resources, not all of them can have all the elements being deleted.
expected := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatever
spec:
template:
spec:
containers:
- env:
- name: EXISTING
value: EXISTING_VALUE
image: helloworld
name: whatever
`
// Allow expected variable to be unused
_ = expected
// Currently, kustomize inserts $patch: delete elements into the resulting resources
erroneousActual := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatever
spec:
template:
spec:
containers:
- env:
- $patch: delete
name: NOT_EXISTING_FOR_REMOVAL
- name: EXISTING
value: EXISTING_VALUE
image: helloworld
name: whatever
`
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, erroneousActual)
}

View File

@@ -0,0 +1,134 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"fmt"
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestPodDisruptionBudgetBasics(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("pdbLiteral.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbLiteral
spec:
maxUnavailable: 90
`)
th.WriteF("pdbPercentage.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbPercentage
spec:
maxUnavailable: 90%
`)
th.WriteK(".", `
resources:
- pdbLiteral.yaml
- pdbPercentage.yaml
`)
m := th.Run(".", th.MakeDefaultOptions())
// In a PodDisruptionBudget, the fields maxUnavailable
// minAvailable are mutually exclusive, and both can hold
// either an integer, i.e. 10, or string that has to be
// an int followed by a percent sign, e.g. 10%.
th.AssertActualEqualsExpected(m, `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbLiteral
spec:
maxUnavailable: 90
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbPercentage
spec:
maxUnavailable: 90%
`)
}
func TestPodDisruptionBudgetMerging(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
th.WriteF("pdb-patch.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: generic-pdb
spec:
maxUnavailable: 1
`)
th.WriteF("my_file.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: championships-api
labels:
faceit-pdb: default
spec:
maxUnavailable: 100%
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: championships-api-2
labels:
faceit-pdb: default
spec:
maxUnavailable: 100%
`)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: pdb-patch.yaml
target:
kind: PodDisruptionBudget
labelSelector: faceit-pdb=default
resources:
- my_file.yaml
`)
m := th.Run(".", opts)
expFmt := `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
labels:
faceit-pdb: default
name: championships-api
spec:
maxUnavailable: %s
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
labels:
faceit-pdb: default
name: championships-api-2
spec:
maxUnavailable: %s
`
// In a PodDisruptionBudget, the fields maxUnavailable
// minAvailable are mutually exclusive, and both can hold
// either an integer, i.e. 10, or string that has to be
// an int followed by a percent sign, e.g. 10%.
// In the former case - bare integer - they should NOT be quoted
// as the api server will reject it. In the latter case with
// the percent sign, quotes can be added and the API server will
// accept it, but they don't have to be added.
th.AssertActualEqualsExpected(
m,
// TODO(#3304): DECISION - kyaml better; not a bug.
opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, `"1"`, `"1"`),
fmt.Sprintf(expFmt, `1`, `1`)))
}

View File

@@ -80,7 +80,7 @@ data:
`)
m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
th.AssertActualEqualsExpectedNoIdAnnotations(m, `
apiVersion: v1
data:
foo: foo

View File

@@ -360,12 +360,189 @@ resources:
}
}
func TestSimpleServicePortVarReplace(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- service.yaml
- statefulset.yaml
vars:
- name: THE_PORT
objref:
kind: StatefulSet
name: cockroachdb
apiVersion: apps/v1beta1
fieldref:
fieldpath: spec.template.spec.containers[0].ports[1].containerPort
`)
th.WriteF("service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
ports:
- port: $(THE_PORT)
targetPort: $(THE_PORT)
name: grpc
`)
th.WriteF("statefulset.yaml", `
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: cockroachdb
spec:
template:
spec:
containers:
- name: cockroachdb
image: cockroachdb/cockroach:v1.1.5
ports:
- containerPort: 26257
name: grpc
- containerPort: 8888
name: http
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
ports:
- name: grpc
port: 8888
targetPort: 8888
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: cockroachdb
spec:
template:
spec:
containers:
- image: cockroachdb/cockroach:v1.1.5
name: cockroachdb
ports:
- containerPort: 26257
name: grpc
- containerPort: 8888
name: http
`)
}
// TODO(3449): Yield bare primitives in var replacements from configmaps.
// The ConfigMap data field is always strings, and anything that looks
// like a boolean or int or float must be quoted, or the API server won't
// accept the map. This creates a problem if one wants to use a var to
// inject a raw number or raw boolean sourced from a configmap, because as
// far as the configmap representation is concerned, it's a string.
// A workaround would be to source the var from another Kind, from a field
// that allowed unquoted vars or booleans.
func TestIssue3449(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- workflow.yaml
configurations:
- kustomization-config.yaml
configMapGenerator:
- name: kustomize-vars
envs:
- vars.env
vars:
- name: DBT_TARGET
objref: &config-map-ref
kind: ConfigMap
name: kustomize-vars
apiVersion: v1
fieldref:
fieldpath: data.DBT_TARGET
- name: SUSPENDED
objref: *config-map-ref
fieldref:
fieldpath: data.SUSPENDED
`)
th.WriteF("workflow.yaml", `
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
name: cron-core-load-workflow
spec:
schedule: "45 2 * * *"
timezone: "Europe/Vienna"
concurrencyPolicy: Forbid
suspend: $(SUSPENDED)
workflowMetadata:
labels:
workflowName: core-load-workflow
workflowSpec:
workflowTemplateRef:
name: core-load-pipeline
arguments:
parameters:
- name: dbt_target
value: $(DBT_TARGET)
`)
th.WriteF("kustomization-config.yaml", `
nameReference:
- kind: ConfigMap
version: v1
fieldSpecs:
- kind: CronWorkflow
version: v1alpha1
path: spec/workflowSpec/arguments/parameters/value
varReference:
- path: spec/workflowSpec/arguments/parameters/value
kind: CronWorkflow
apiVersion: argoproj.io/v1alpha1
- path: spec
kind: CronWorkflow
apiVersion: argoproj.io/v1alpha1
`)
th.WriteF("vars.env", `
DBT_TARGET=development
SUSPENDED=True
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
name: cron-core-load-workflow
spec:
concurrencyPolicy: Forbid
schedule: 45 2 * * *
suspend: "True"
timezone: Europe/Vienna
workflowMetadata:
labels:
workflowName: core-load-workflow
workflowSpec:
arguments:
parameters:
- name: dbt_target
value: development
workflowTemplateRef:
name: core-load-pipeline
---
apiVersion: v1
data:
DBT_TARGET: development
SUSPENDED: "True"
kind: ConfigMap
metadata:
name: kustomize-vars-7mhm8cg5kg
`)
}
func TestVarRefBig(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
if opts.UseKyaml {
t.Skip("TODO(#3396)")
}
th.WriteK("/app/base", `
namePrefix: base-
resources:
@@ -682,7 +859,7 @@ namePrefix: dev-
resources:
- ../../base
`)
m := th.Run("/app/overlay/staging", opts)
m := th.Run("/app/overlay/staging", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ServiceAccount
@@ -929,7 +1106,64 @@ metadata:
`)
}
func TestVariableRefIngress(t *testing.T) {
func TestVariableRefIngressBasic(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- ingress.yaml
- deployment.yaml
vars:
- name: DEPLOYMENT_NAME
objref:
apiVersion: apps/v1
kind: Deployment
name: nginxDep
fieldref:
fieldpath: metadata.name
`)
th.WriteF("deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginxDep
`)
th.WriteF("ingress.yaml", `
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginxIngress
spec:
rules:
- host: $(DEPLOYMENT_NAME).example.com
tls:
- hosts:
- $(DEPLOYMENT_NAME).example.com
secretName: $(DEPLOYMENT_NAME).example.com-tls
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginxIngress
spec:
rules:
- host: nginxDep.example.com
tls:
- hosts:
- nginxDep.example.com
secretName: nginxDep.example.com-tls
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginxDep
`)
}
func TestVariableRefIngressOverlay(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", `
resources:
@@ -1976,67 +2210,64 @@ spec:
func TestDeploymentAnnotations(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
th.WriteK(".", `
configMapGenerator:
- name: testConfigMap
envs:
- test.properties
- name: theConfigMap
envs:
- test.properties
vars:
- name: FOO
objref:
kind: ConfigMap
name: testConfigMap
apiVersion: v1
fieldref:
fieldpath: data.foo
- name: SOMERIVER
objref:
kind: ConfigMap
name: theConfigMap
apiVersion: v1
fieldref:
fieldpath: data.waterway
commonAnnotations:
foo: $(FOO)
river: $(SOMERIVER)
resources:
- deployment.yaml
- deployment.yaml
`)
th.WriteF("/app/deployment.yaml", `
th.WriteF("deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
name: theDeployment
spec:
template:
spec:
containers:
- name: test
- name: test
`)
th.WriteF("/app/test.properties", `foo=bar`)
m := th.Run("/app", th.MakeDefaultOptions())
th.WriteF("test.properties", `waterway=mississippi`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
foo: bar
name: test
river: mississippi
name: theDeployment
spec:
template:
metadata:
annotations:
foo: bar
river: mississippi
spec:
containers:
- name: test
---
apiVersion: v1
data:
foo: bar
waterway: mississippi
kind: ConfigMap
metadata:
annotations:
foo: bar
name: testConfigMap-798k5k7g9f
river: mississippi
name: theConfigMap-hdd8h8cgdt
`)
}

View File

@@ -389,10 +389,10 @@ spec:
rm, err := rmF.ConflatePatches([]*resource.Resource{r1, r2})
assert.NoError(t, err)
yml, err = rm.AsYaml()
assert.NoError(t, err)
// TODO(#3304): DECISION - kyaml better; not a bug.
assert.Equal(t, konfig.IfApiMachineryElseKyaml(`apiVersion: example.com/v1
kind: Foo
metadata:

View File

@@ -245,4 +245,7 @@ type ResMap interface {
// selected set of resources.
ApplySmPatch(
selectedSet *resource.IdSet, patch *resource.Resource) error
// RemoveBuildAnnotations removes annotations created by the build process.
RemoveBuildAnnotations()
}

View File

@@ -589,6 +589,7 @@ func (m *resWrangler) ApplySmPatch(
patchCopy.SetName(res.GetName())
patchCopy.SetNamespace(res.GetNamespace())
patchCopy.SetGvk(res.GetGvk())
patchCopy.SetOriginalName(res.GetOriginalName(), true)
err := res.ApplySmPatch(patchCopy)
if err != nil {
// Check for an error string from UnmarshalJSON that's indicative
@@ -619,3 +620,9 @@ func (m *resWrangler) ApplySmPatch(
m.AppendAll(newRm)
return nil
}
func (m *resWrangler) RemoveBuildAnnotations() {
for _, r := range m.Resources() {
r.RemoveBuildAnnotations()
}
}

View File

@@ -745,7 +745,6 @@ rules:
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
rnodes, err := rm.ToRNodeSlice()
if err != nil {
t.Fatalf("unexpected error: %v", err)
@@ -999,6 +998,7 @@ spec:
return
}
assert.False(t, tc.errorExpected)
m.RemoveBuildAnnotations()
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, strings.Join(tc.expected, "---\n"), string(yml))
@@ -1100,18 +1100,22 @@ $patch: delete
finalMapSize: 0,
},
}
for name, test := range tests {
m, err := rmF.NewResMapFromBytes([]byte(target))
assert.NoError(t, err, name)
idSet := resource.MakeIdSet(m.Resources())
assert.Equal(t, 1, idSet.Size(), name)
p, err := rf.FromBytes([]byte(test.patch))
assert.NoError(t, err, name)
assert.NoError(t, m.ApplySmPatch(idSet, p), name)
assert.Equal(t, test.finalMapSize, m.Size(), name)
yml, err := m.AsYaml()
assert.NoError(t, err, name)
assert.Equal(t, test.expected, string(yml), name)
for name := range tests {
tc := tests[name]
t.Run(name, func(t *testing.T) {
m, err := rmF.NewResMapFromBytes([]byte(target))
assert.NoError(t, err, name)
idSet := resource.MakeIdSet(m.Resources())
assert.Equal(t, 1, idSet.Size(), name)
p, err := rf.FromBytes([]byte(tc.patch))
assert.NoError(t, err, name)
assert.NoError(t, m.ApplySmPatch(idSet, p), name)
assert.Equal(t, tc.finalMapSize, m.Size(), name)
m.RemoveBuildAnnotations()
yml, err := m.AsYaml()
assert.NoError(t, err, name)
assert.Equal(t, tc.expected, string(yml), name)
})
}
}

View File

@@ -35,17 +35,17 @@ func (rf *Factory) FromMap(m map[string]interface{}) *Resource {
// FromMapWithName returns a new instance with the given "original" name.
func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalName(n)
return rf.makeOne(rf.kf.FromMap(m), nil).SetOriginalName(n, true)
}
// FromMapWithNamespace returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespace(n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(n)
return rf.makeOne(rf.kf.FromMap(m), nil).SetOriginalNs(n, true)
}
// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(ns).setOriginalName(n)
return rf.makeOne(rf.kf.FromMap(m), nil).SetOriginalNs(ns, true).SetOriginalName(n, true)
}
// FromMapAndOption returns a new instance of Resource with given options.
@@ -72,7 +72,7 @@ func (rf *Factory) makeOne(
kunStr: u,
options: o,
}
return r.setOriginalName(r.kunStr.GetName()).setOriginalNs(r.GetNamespace())
return r
}
// SliceFromPatches returns a slice of resources given a patch path
@@ -157,7 +157,7 @@ func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resour
return nil, fmt.Errorf("number of names doesn't match number of resources")
}
for i, res := range result {
res.originalName = names[i]
res.SetOriginalName(names[i], true)
}
return result, nil
}

View File

@@ -343,14 +343,13 @@ kind: List
name: "listWithAnchorReference",
input: []types.PatchStrategicMerge{patchList2},
expectedOut: []*Resource{testDeploymentA, testDeploymentB},
// See https://github.com/kubernetes-sigs/kustomize/issues/3271
// This test should not have an error, but does when kyaml is used.
// The error using kyaml is:
// json: unsupported type: map[interface {}]interface {}
// probably arising from too many conversions between
// maybe arising from too many conversions between
// yaml, json, Resource, RNode, Unstructured etc.
// These conversions can be removed after closing
// https://github.com/kubernetes-sigs/kustomize/issues/2506
// These conversions go away after closing #3506
// TODO(#3271) This shouldn't have an error, but does when kyaml is used.
// TODO(#3304): DECISION - still a bug, but not a blocker to #3304 or #2506
expectedErr: konfig.FlagEnableKyamlDefaultValue,
},
{

View File

@@ -10,9 +10,13 @@ import (
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/wrappy"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml"
)
@@ -20,16 +24,19 @@ import (
// 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
originalNs string
options *types.GenArgs
refBy []resid.ResId
refVarNames []string
namePrefixes []string
nameSuffixes []string
kunStr ifc.Kunstructured
options *types.GenArgs
refBy []resid.ResId
refVarNames []string
}
const (
buildAnnotationOriginalName = konfig.ConfigAnnoDomain + "/originalName"
buildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
buildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
buildAnnotationOriginalNamespace = konfig.ConfigAnnoDomain + "/originalNs"
)
func (r *Resource) ResetPrimaryData(incoming *Resource) {
r.kunStr = incoming.Copy()
}
@@ -169,13 +176,9 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) {
}
func (r *Resource) copyOtherFields(other *Resource) {
r.originalName = other.originalName
r.originalNs = other.originalNs
r.options = other.options
r.refBy = other.copyRefBy()
r.refVarNames = copyStringSlice(other.refVarNames)
r.namePrefixes = copyStringSlice(other.namePrefixes)
r.nameSuffixes = copyStringSlice(other.nameSuffixes)
}
func (r *Resource) MergeDataMapFrom(o *Resource) {
@@ -242,28 +245,46 @@ func copyStringSlice(s []string) []string {
// Implements ResCtx AddNamePrefix
func (r *Resource) AddNamePrefix(p string) {
r.namePrefixes = append(r.namePrefixes, p)
r.addAdditiveAnnotation(buildAnnotationPrefixes, p)
}
// Implements ResCtx AddNameSuffix
func (r *Resource) AddNameSuffix(s string) {
r.nameSuffixes = append(r.nameSuffixes, s)
r.addAdditiveAnnotation(buildAnnotationSuffixes, s)
}
func (r *Resource) addAdditiveAnnotation(name, value string) {
if value == "" {
return
}
annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
if existing, ok := annotations[name]; ok {
annotations[name] = existing + "," + value
} else {
annotations[name] = value
}
r.SetAnnotations(annotations)
}
// Implements ResCtx GetOutermostNamePrefix
func (r *Resource) GetOutermostNamePrefix() string {
if len(r.namePrefixes) == 0 {
namePrefixes := r.GetNamePrefixes()
if len(namePrefixes) == 0 {
return ""
}
return r.namePrefixes[len(r.namePrefixes)-1]
return namePrefixes[len(namePrefixes)-1]
}
// Implements ResCtx GetOutermostNameSuffix
func (r *Resource) GetOutermostNameSuffix() string {
if len(r.nameSuffixes) == 0 {
nameSuffixes := r.GetNameSuffixes()
if len(nameSuffixes) == 0 {
return ""
}
return r.nameSuffixes[len(r.nameSuffixes)-1]
return nameSuffixes[len(nameSuffixes)-1]
}
func sameEndingSubarray(a, b []string) bool {
@@ -288,12 +309,20 @@ func sameEndingSubarray(a, b []string) bool {
// Implements ResCtx GetNamePrefixes
func (r *Resource) GetNamePrefixes() []string {
return r.namePrefixes
annotations := r.GetAnnotations()
if _, ok := annotations[buildAnnotationPrefixes]; !ok {
return nil
}
return strings.Split(annotations[buildAnnotationPrefixes], ",")
}
// Implements ResCtx GetNameSuffixes
func (r *Resource) GetNameSuffixes() []string {
return r.nameSuffixes
annotations := r.GetAnnotations()
if _, ok := annotations[buildAnnotationSuffixes]; !ok {
return nil
}
return strings.Split(annotations[buildAnnotationSuffixes], ",")
}
// OutermostPrefixSuffixEquals returns true if both resources
@@ -317,23 +346,65 @@ func (r *Resource) PrefixesSuffixesEquals(o ResCtx) bool {
return sameEndingSubarray(r.GetNamePrefixes(), o.GetNamePrefixes()) && sameEndingSubarray(r.GetNameSuffixes(), o.GetNameSuffixes())
}
func (r *Resource) GetOriginalName() string {
return r.originalName
// RemoveBuildAnnotations removes annotations created by the build process.
// These are internal-only to kustomize, added to the data pipeline to
// track name changes so name references can be fixed.
func (r *Resource) RemoveBuildAnnotations() {
annotations := r.GetAnnotations()
if len(annotations) == 0 {
return
}
delete(annotations, buildAnnotationOriginalName)
delete(annotations, buildAnnotationPrefixes)
delete(annotations, buildAnnotationSuffixes)
delete(annotations, buildAnnotationOriginalNamespace)
r.SetAnnotations(annotations)
}
// Making this public would be bad.
func (r *Resource) setOriginalName(n string) *Resource {
r.originalName = n
func (r *Resource) GetOriginalName() string {
annotations := r.GetAnnotations()
if name, ok := annotations[buildAnnotationOriginalName]; ok {
return name
}
return r.kunStr.GetName()
}
func (r *Resource) SetOriginalName(n string, overwrite bool) *Resource {
annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
if _, ok := annotations[buildAnnotationOriginalName]; !ok || overwrite {
annotations[buildAnnotationOriginalName] = n
}
r.kunStr.SetAnnotations(annotations)
return r
}
func (r *Resource) GetOriginalNs() string {
return r.originalNs
annotations := r.GetAnnotations()
if ns, ok := annotations[buildAnnotationOriginalNamespace]; ok {
return ns
}
ns := r.GetNamespace()
if ns == "default" {
return ""
}
return ns
}
// Making this public would be bad.
func (r *Resource) setOriginalNs(n string) *Resource {
r.originalNs = n
func (r *Resource) SetOriginalNs(n string, overwrite bool) *Resource {
if n == "" {
n = "default"
}
annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
if _, ok := annotations[buildAnnotationOriginalNamespace]; !ok || overwrite {
annotations[buildAnnotationOriginalNamespace] = n
}
r.SetAnnotations(annotations)
return r
}
@@ -422,9 +493,12 @@ func (r *Resource) ApplySmPatch(patch *Resource) error {
return err
}
n, ns := r.GetName(), r.GetNamespace()
err = filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
err = r.ApplyFilter(patchstrategicmerge.Filter{
Patch: node,
}, r)
})
if err != nil {
return err
}
if !r.IsEmpty() {
r.SetName(n)
r.SetNamespace(ns)
@@ -432,6 +506,18 @@ func (r *Resource) ApplySmPatch(patch *Resource) error {
return err
}
func (r *Resource) ApplyFilter(f kio.Filter) error {
if wn, ok := r.kunStr.(*wrappy.WNode); ok {
l, err := f.Filter([]*kyaml.RNode{wn.AsRNode()})
if len(l) == 0 {
// Hack to deal with deletion.
r.kunStr = wrappy.NewWNode()
}
return err
}
return filtersutil.ApplyToJSON(f, r)
}
func mergeStringMaps(maps ...map[string]string) map[string]string {
result := map[string]string{}
for _, m := range maps {

View File

@@ -695,6 +695,325 @@ spec:
}
}
func TestSetOriginalNameAndNs(t *testing.T) {
input := `apiVersion: apps/v1
kind: Secret
metadata:
name: newName`
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(input))
if err != nil {
t.Fatal(err)
}
res := resources[0]
res.SetOriginalName("oldName", false)
res.SetOriginalNs("default", false)
expected := `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
config.kubernetes.io/originalNs: default
name: newName
`
bytes, err := res.AsYAML()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, expected, string(bytes))
}
func TestGetOriginalName(t *testing.T) {
tests := []struct {
input string
expected string
}{
{
// no name annotation, return the name
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: mySecret`,
expected: "mySecret",
},
{
// return name from name annotation
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName`,
expected: "oldName",
},
}
for _, test := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(test.input))
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, resources[0].GetOriginalName())
}
}
func TestSetOriginalName(t *testing.T) {
tests := []struct {
input string
originalName string
overwrite bool
expected string
}{
{
// no original name set, overwrite is false
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName`,
originalName: "oldName",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName
`,
},
{
// no original name set, overwrite is true
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName`,
originalName: "oldName",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName
`,
},
{
// original name is set, overwrite is false, resource shouldn't change
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName`,
originalName: "newOriginalName",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName
`,
},
{
// original name is set, overwrite is true, resource should change
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName`,
originalName: "newOriginalName",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: newOriginalName
name: newName
`,
},
}
for _, test := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(test.input))
if err != nil {
t.Fatal(err)
}
res := resources[0]
res.SetOriginalName(test.originalName, test.overwrite)
bytes, err := res.AsYAML()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, string(bytes))
}
}
func TestGetOriginalNs(t *testing.T) {
tests := []struct {
input string
expected string
}{
{
// no namespace, return default
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: mySecret`,
expected: "",
},
{
// return old namespace
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: mySecret
namespace: myNamespace`,
expected: "oldNamespace",
},
{
// return namespace
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: mySecret
namespace: myNamespace`,
expected: "myNamespace",
},
}
for _, test := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(test.input))
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, resources[0].GetOriginalNs())
}
}
func TestSetOriginalNs(t *testing.T) {
tests := []struct {
input string
originalNs string
overwrite bool
expected string
}{
{
// no original namespace set, overwrite is false
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName
namespace: newNamespace`,
originalNs: "oldNamespace",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace
`,
},
{
// no original name set, overwrite is true
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName
namespace: newNamespace`,
originalNs: "oldNamespace",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace
`,
},
{
// original name is set, overwrite is false, resource shouldn't change
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace`,
originalNs: "newOriginalNamespace",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace
`,
},
{
// original name is set, overwrite is true, resource should change
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace`,
originalNs: "newOriginalNamespace",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: newOriginalNamespace
name: newName
namespace: newNamespace
`,
},
}
for _, test := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(test.input))
if err != nil {
t.Fatal(err)
}
res := resources[0]
res.SetOriginalNs(test.originalNs, test.overwrite)
bytes, err := res.AsYAML()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, string(bytes))
}
}
// baseResource produces a base object which used to test
// patch transformation
// Also the structure is matching the Deployment syntax

View File

@@ -126,6 +126,11 @@ func (th Harness) AssertActualEqualsExpected(
th.AssertActualEqualsExpectedWithTweak(m, nil, expected)
}
func (th Harness) AssertActualEqualsExpectedNoIdAnnotations(m resmap.ResMap, expected string) {
m.RemoveBuildAnnotations()
th.AssertActualEqualsExpectedWithTweak(m, nil, expected)
}
func (th Harness) AssertActualEqualsExpectedWithTweak(
m resmap.ResMap, tweaker func([]byte) []byte, expected string) {
assertActualEqualsExpectedWithTweak(th, m, tweaker, expected)

View File

@@ -109,6 +109,7 @@ func (th *HarnessEnhanced) LoadAndRunGenerator(
if err != nil {
th.t.Fatalf("Err: %v", err)
}
rm.RemoveBuildAnnotations()
return rm
}
@@ -124,7 +125,7 @@ func (th *HarnessEnhanced) LoadAndRunTransformer(
func (th *HarnessEnhanced) RunTransformerAndCheckResult(
config, input, expected string) {
resMap := th.LoadAndRunTransformer(config, input)
th.AssertActualEqualsExpected(resMap, expected)
th.AssertActualEqualsExpectedNoIdAnnotations(resMap, expected)
}
func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(

View File

@@ -15,7 +15,5 @@ require (
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
k8s.io/apimachinery v0.18.10
sigs.k8s.io/kustomize/kyaml v0.10.3
sigs.k8s.io/kustomize/kyaml v0.10.6
)
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml

View File

@@ -378,8 +378,6 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -390,6 +388,8 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
sigs.k8s.io/kustomize/kyaml v0.10.6 h1:xUJxc/k8JoWqHUahaB8DTqY0KwEPxTbTGStvW8TOcDc=
sigs.k8s.io/kustomize/kyaml v0.10.6/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=

View File

@@ -54,6 +54,7 @@ func (e *Editor) Tidy() error {
func (e *Editor) Pin(target misc.LaModule, oldV, newV semver.SemVer) error {
err := e.run(
"edit",
"-dropreplace="+target.ImportPath(),
"-dropreplace="+target.ImportPath()+"@"+oldV.String(),
"-require="+target.ImportPath()+"@"+newV.String(),
)

View File

@@ -6,11 +6,9 @@ require (
github.com/rakyll/statik v0.1.7
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.4.0
sigs.k8s.io/kustomize/api v0.7.0
sigs.k8s.io/kustomize/kyaml v0.10.3
sigs.k8s.io/kustomize/api v0.7.1
sigs.k8s.io/kustomize/kyaml v0.10.6
sigs.k8s.io/yaml v1.2.0
)
replace sigs.k8s.io/kustomize/api => ../../api
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml

View File

@@ -512,8 +512,8 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
@@ -534,6 +534,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.10.6 h1:xUJxc/k8JoWqHUahaB8DTqY0KwEPxTbTGStvW8TOcDc=
sigs.k8s.io/kustomize/kyaml v0.10.6/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
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,2 +1,2 @@
Moved to [https://kubernetes-sigs.github.io/kustomize](https://kubernetes-sigs.github.io/kustomize/guides/plugins)
Moved to [https://kubernetes-sigs.github.io/kustomize](https://kubectl.docs.kubernetes.io/guides/extending_kustomize/)

View File

@@ -34,7 +34,7 @@ function runTest {
fi
rcAccumulator=$((rcAccumulator || $code))
if [ $code -ne 0 ]; then
echo "Failure in $d"
echo "**** FAILURE in $d"
fi
}
@@ -61,8 +61,6 @@ for goMod in $(find ./plugin -name 'go.mod' -not -path "./plugin/untested/*"); d
done
if [ $rcAccumulator -ne 0 ]; then
echo "FAILURE; exit code $rcAccumulator"
echo "FAIL; exit code $rcAccumulator"
exit 1
fi

View File

@@ -8,9 +8,9 @@ require (
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
k8s.io/client-go v0.18.10
sigs.k8s.io/kustomize/api v0.7.0
sigs.k8s.io/kustomize/cmd/config v0.8.6
sigs.k8s.io/kustomize/kyaml v0.10.3
sigs.k8s.io/kustomize/api v0.7.1
sigs.k8s.io/kustomize/cmd/config v0.8.8
sigs.k8s.io/kustomize/kyaml v0.10.6
sigs.k8s.io/yaml v1.2.0
)
@@ -21,5 +21,3 @@ exclude (
)
replace sigs.k8s.io/kustomize/api => ../api
replace sigs.k8s.io/kustomize/kyaml => ../kyaml

View File

@@ -592,8 +592,6 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -624,8 +622,10 @@ k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/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/cmd/config v0.8.6 h1:Rr7eyD+h32OfruN6V+cgUqHRpC2Y5ZnjjAPbjhKFLGE=
sigs.k8s.io/kustomize/cmd/config v0.8.6/go.mod h1:e4PgdLUNnkf+Iapvjyb6gTG9DZQkDZIR6uS1Bv4YA6s=
sigs.k8s.io/kustomize/cmd/config v0.8.8 h1:B0ecq4yYrD1zcigW7E9xOtv40D/87vokzlNzhkROxKM=
sigs.k8s.io/kustomize/cmd/config v0.8.8/go.mod h1:SUgpGFAeXIIJua6SIGsMgXpNCx5eiMbl7wNlm57KXt4=
sigs.k8s.io/kustomize/kyaml v0.10.6 h1:xUJxc/k8JoWqHUahaB8DTqY0KwEPxTbTGStvW8TOcDc=
sigs.k8s.io/kustomize/kyaml v0.10.6/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e h1:4Z09Hglb792X0kfOBBJUPFEyvVfQWrYT/l8h5EKA6JQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=

View File

@@ -7,7 +7,7 @@ import (
"fmt"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/krusty"
)
// NewCmdListBuiltinPlugin return an instance of list-builtin-plugin
@@ -18,7 +18,7 @@ func NewCmdListBuiltinPlugin() *cobra.Command {
Short: "[Alpha] List the builtin plugins",
Long: "",
Run: func(cmd *cobra.Command, args []string) {
plugins := konfig.GetBuiltinPluginNames()
plugins := krusty.GetBuiltinPluginNames()
fmt.Print("Builtin plugins:\n\n")
for _, p := range plugins {
fmt.Printf(" * %s\n", p)

View File

@@ -8,6 +8,7 @@ require (
github.com/go-openapi/spec v0.19.5
github.com/go-openapi/strfmt v0.19.5
github.com/go-openapi/validate v0.19.8
github.com/google/go-cmp v0.3.0
github.com/markbates/pkger v0.17.1
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d
@@ -19,5 +20,5 @@ require (
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 // indirect
gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
)

View File

@@ -295,6 +295,6 @@ 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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -149,6 +149,11 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
index := 0
for i := range values {
// the Split used above will eat the tail '\n' from each resource. This may affect the
// literal string value since '\n' is meaningful in it.
if i != len(values)-1 {
values[i] += "\n"
}
decoder := yaml.NewDecoder(bytes.NewBufferString(values[i]))
node, err := r.decode(index, decoder)
if err == io.EOF {
@@ -173,7 +178,7 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
if !r.DisableUnwrapping &&
len(values) == 1 && // Only unwrap if there is only 1 value
(meta.Kind == ResourceListKind || meta.Kind == "List") &&
node.Field("items") != nil {
(node.Field("items") != nil || node.Field("functionConfig") != nil) {
r.WrappingKind = meta.Kind
r.WrappingAPIVersion = meta.APIVersion

View File

@@ -94,6 +94,29 @@ spec:
elems:
- a
- b
- c`,
wrappingAPIVersion: ResourceListAPIVersion,
wrappingAPIKind: ResourceListKind,
},
//
//
//
{
name: "wrapped_resource_list_function_config_without_items",
input: `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
functionConfig:
foo: bar
elems:
- a
- b
- c
`,
expectedItems: []string{},
expectedFunctionConfig: `foo: bar
elems:
- a
- b
- c`,
wrappingAPIVersion: ResourceListAPIVersion,
wrappingAPIKind: ResourceListKind,

107
kyaml/yaml/datamap.go Normal file
View File

@@ -0,0 +1,107 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml
import (
"encoding/base64"
"sort"
"strings"
"unicode/utf8"
)
// SortedMapKeys returns a sorted slice of keys to the given map.
// Writing this function never gets old.
func SortedMapKeys(m map[string]string) []string {
keys := make([]string, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
func (rn *RNode) LoadMapIntoConfigMapData(m map[string]string) error {
for _, k := range SortedMapKeys(m) {
fldName, vrN := makeConfigMapValueRNode(m[k])
if _, err := rn.Pipe(
LookupCreate(MappingNode, fldName),
SetField(k, vrN)); err != nil {
return err
}
}
return nil
}
func makeConfigMapValueRNode(s string) (field string, rN *RNode) {
yN := &Node{Kind: ScalarNode}
yN.Tag = NodeTagString
if utf8.ValidString(s) {
field = DataField
yN.Value = s
} else {
field = BinaryDataField
yN.Value = encodeBase64(s)
}
if strings.Contains(yN.Value, "\n") {
yN.Style = LiteralStyle
}
return field, NewRNode(yN)
}
func (rn *RNode) LoadMapIntoSecretData(m map[string]string) error {
mapNode, err := rn.Pipe(LookupCreate(MappingNode, DataField))
if err != nil {
return err
}
for _, k := range SortedMapKeys(m) {
vrN := makeSecretValueRNode(m[k])
if _, err := mapNode.Pipe(SetField(k, vrN)); err != nil {
return err
}
}
return nil
}
// In a secret, all data is base64 encoded, regardless of its conformance
// or lack thereof to UTF-8.
func makeSecretValueRNode(s string) *RNode {
yN := &Node{Kind: ScalarNode}
// Purposely don't use YAML tags to identify the data as being plain text or
// binary. It kubernetes Secrets the values in the `data` map are expected
// to be base64 encoded, and in ConfigMaps that same can be said for the
// values in the `binaryData` field.
yN.Tag = NodeTagString
yN.Value = encodeBase64(s)
if strings.Contains(yN.Value, "\n") {
yN.Style = LiteralStyle
}
return NewRNode(yN)
}
// encodeBase64 encodes s as base64 that is broken up into multiple lines
// as appropriate for the resulting length.
func encodeBase64(s string) string {
const lineLen = 70
encLen := base64.StdEncoding.EncodedLen(len(s))
lines := encLen/lineLen + 1
buf := make([]byte, encLen*2+lines)
in := buf[0:encLen]
out := buf[encLen:]
base64.StdEncoding.Encode(in, []byte(s))
k := 0
for i := 0; i < len(in); i += lineLen {
j := i + lineLen
if j > len(in) {
j = len(in)
}
k += copy(out[k:], in[i:j])
if lines > 1 {
out[k] = '\n'
k++
}
}
return string(out[:k])
}

View File

@@ -1,10 +1,13 @@
package filtersutil_test
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package yaml_test
import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestSortedKeys(t *testing.T) {
@@ -23,9 +26,10 @@ func TestSortedKeys(t *testing.T) {
expected: []string{"a", "b", "c"}},
}
for tn, tc := range testCases {
tc := tc
t.Run(tn, func(t *testing.T) {
if !assert.Equal(t,
filtersutil.SortedMapKeys(tc.input),
yaml.SortedMapKeys(tc.input),
tc.expected) {
t.FailNow()
}

View File

@@ -648,9 +648,15 @@ func (s FieldSetter) Filter(rn *RNode) (*RNode, error) {
}
// create the field
rn.YNode().Content = append(rn.YNode().Content,
&yaml.Node{Kind: yaml.ScalarNode, HeadComment: s.Comments.HeadComment,
LineComment: s.Comments.LineComment, FootComment: s.Comments.FootComment, Value: s.Name},
rn.YNode().Content = append(
rn.YNode().Content,
&yaml.Node{
Kind: yaml.ScalarNode,
Value: s.Name,
HeadComment: s.Comments.HeadComment,
LineComment: s.Comments.LineComment,
FootComment: s.Comments.FootComment,
},
s.Value.YNode())
return s.Value, nil
}

View File

@@ -723,7 +723,7 @@ j: k
assert.Nil(t, rn)
}
func TestSetField_Fn(t *testing.T) {
func TestFieldSetter(t *testing.T) {
// Change field
node, err := Parse(`
foo: baz
@@ -803,6 +803,40 @@ foo
assert.Nil(t, k)
}
func TestFieldSetterNumberInKeyRegression(t *testing.T) {
node := NewMapRNode(&map[string]string{"river": "mississippi"})
k, err := FieldSetter{
Name: "forty 2",
Value: NewScalarRNode("number key one"),
}.Filter(node)
assert.NoError(t, err)
assert.Equal(t, `number key one
`, assertNoErrorString(t)(k.String()))
k, err = FieldSetter{
Name: "fortytwo",
Value: NewScalarRNode("number key two"),
}.Filter(node)
assert.NoError(t, err)
assert.Equal(t, `number key two
`, assertNoErrorString(t)(k.String()))
k, err = FieldSetter{
Name: "42",
Value: NewScalarRNode("number key three"),
}.Filter(node)
assert.NoError(t, err)
assert.Equal(t, `number key three
`, assertNoErrorString(t)(k.String()))
assert.Equal(t, `river: mississippi
forty 2: number key one
fortytwo: number key two
42: number key three
`, assertNoErrorString(t)(node.String()))
}
func TestSet_Fn(t *testing.T) {
node, err := Parse(`
foo: baz

View File

@@ -372,18 +372,7 @@ func (rn *RNode) GetAnnotations() (map[string]string, error) {
// SetAnnotations tries to set the metadata annotations field.
func (rn *RNode) SetAnnotations(m map[string]string) error {
meta, err := rn.Pipe(Lookup(MetadataField))
if err != nil {
return err
}
if len(m) == 0 {
if meta == nil {
return nil
}
return meta.PipeE(Clear(AnnotationsField))
}
return rn.SetMapField(
NewMapRNode(&m), MetadataField, AnnotationsField)
return rn.setMapInMetadata(m, AnnotationsField)
}
// GetLabels gets the metadata labels field.
@@ -397,18 +386,32 @@ func (rn *RNode) GetLabels() (map[string]string, error) {
// SetLabels sets the metadata labels field.
func (rn *RNode) SetLabels(m map[string]string) error {
return rn.setMapInMetadata(m, LabelsField)
}
// This established proper quoting on string values, and sorts by key.
func (rn *RNode) setMapInMetadata(m map[string]string, field string) error {
meta, err := rn.Pipe(Lookup(MetadataField))
if err != nil {
return err
}
if len(m) == 0 {
if meta == nil {
return nil
}
return meta.PipeE(Clear(LabelsField))
if err = meta.PipeE(Clear(field)); err != nil {
return err
}
return rn.SetMapField(
NewMapRNode(&m), MetadataField, LabelsField)
if len(m) == 0 {
return nil
}
mapNode, err := meta.Pipe(LookupCreate(MappingNode, field))
if err != nil {
return err
}
for _, k := range SortedMapKeys(m) {
if _, err := mapNode.Pipe(
SetField(k, NewStringRNode(m[k]))); err != nil {
return err
}
}
return nil
}
func (rn *RNode) SetMapField(value *RNode, path ...string) error {
@@ -435,13 +438,13 @@ func (rn *RNode) SetDataMap(m map[string]string) {
if rn == nil {
log.Fatal("cannot set data map on nil Rnode")
}
if err := rn.PipeE(Clear(DataField)); err != nil {
log.Fatal(err)
}
if len(m) == 0 {
if err := rn.PipeE(Clear(DataField)); err != nil {
log.Fatal(err)
}
return
}
if err := rn.SetMapField(NewMapRNode(&m), DataField); err != nil {
if err := rn.LoadMapIntoConfigMapData(m); err != nil {
log.Fatal(err)
}
}
@@ -785,6 +788,19 @@ func FromMap(m map[string]interface{}) (*RNode, error) {
return Parse(string(c))
}
func (rn *RNode) Map() map[string]interface{} {
if rn == nil || rn.value == nil {
return make(map[string]interface{})
}
var result map[string]interface{}
if err := rn.value.Decode(&result); err != nil {
// Should not be able to create an RNode that cannot be decoded;
// this is an unrecoverable error.
log.Fatalf("failed to decode ynode: %v", err)
}
return result
}
// ConvertJSONToYamlNode parses input json string and returns equivalent yaml node
func ConvertJSONToYamlNode(jsonStr string) (*RNode, error) {
var body map[string]interface{}

View File

@@ -8,6 +8,7 @@ import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
)
@@ -384,6 +385,38 @@ func TestRNodeGetValidatedMetadata(t *testing.T) {
}
}
func TestRNodeMapEmpty(t *testing.T) {
assert.Equal(t, 0, len(NewRNode(nil).Map()))
}
func TestRNodeMap(t *testing.T) {
wn := NewRNode(nil)
if err := wn.UnmarshalJSON([]byte(`{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "homer",
"namespace": "simpsons"
}
}`)); err != nil {
t.Fatalf("unexpected unmarshaljson err: %v", err)
}
expected := map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "homer",
"namespace": "simpsons",
},
}
actual := wn.Map()
if diff := cmp.Diff(expected, actual); diff != "" {
t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
}
}
func TestRNodeFromMap(t *testing.T) {
testConfigMap := map[string]interface{}{
"apiVersion": "v1",

View File

@@ -51,7 +51,8 @@ func IsYNodeEmptyDoc(n *yaml.Node) bool {
}
func IsYNodeString(n *yaml.Node) bool {
return n.Kind == yaml.ScalarNode && n.Tag == NodeTagString
return n.Kind == yaml.ScalarNode &&
(n.Tag == NodeTagString || n.Tag == NodeTagEmpty)
}
// IsYNodeZero is true if all the public fields in the Node are empty.

View File

@@ -58,6 +58,27 @@ func TestCopyYNode(t *testing.T) {
}
}
func TestIsYNodeString(t *testing.T) {
if IsYNodeTaggedNull(nil) {
t.Fatalf("nil cannot be tagged null")
}
if IsYNodeTaggedNull(&Node{}) {
t.Fatalf("untagged node is not tagged")
}
if IsYNodeString(&Node{Tag: NodeTagString}) {
t.Fatalf("non-scalar node is not a string")
}
if IsYNodeString(&Node{Kind: ScalarNode, Tag: NodeTagFloat}) {
t.Fatalf("float tagged node is not tagged")
}
if !IsYNodeString(&Node{Kind: ScalarNode}) {
t.Fatalf("this looks like a string - no tag implies string")
}
if !IsYNodeString(&Node{Kind: ScalarNode, Tag: NodeTagString}) {
t.Fatalf("this looks like a string")
}
}
func TestIsYNodeTaggedNull(t *testing.T) {
if IsYNodeTaggedNull(nil) {
t.Fatalf("nil cannot be tagged null")

View File

@@ -8,7 +8,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/annotations"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -29,11 +28,14 @@ func (p *plugin) Config(
}
func (p *plugin) Transform(m resmap.ResMap) error {
if len(p.Annotations) == 0 {
return nil
}
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(annotations.Filter{
err := r.ApplyFilter(annotations.Filter{
Annotations: p.Annotations,
FsSlice: p.FieldSpecs,
}, r)
})
if err != nil {
return err
}

View File

@@ -9,8 +9,12 @@ import (
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
var (
config = `
func TestAnnotationsTransformer(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
PrepBuiltin("AnnotationsTransformer")
defer th.Reset()
th.RunTransformerAndCheckResult(`
apiVersion: builtin
kind: AnnotationsTransformer
metadata:
@@ -18,11 +22,14 @@ metadata:
annotations:
app: myApp
greeting/morning: a string with blanks
booleanNaked: true
booleanQuoted: "true"
numberNaked: 42
numberQuoted: "42"
fieldSpecs:
- path: metadata/annotations
create: true
`
input = `
`, `
apiVersion: v1
kind: Service
metadata:
@@ -30,25 +37,20 @@ metadata:
spec:
ports:
- port: 7002
`
expectedOutput = `
`, `
apiVersion: v1
kind: Service
metadata:
annotations:
app: myApp
booleanNaked: "true"
booleanQuoted: "true"
greeting/morning: a string with blanks
numberNaked: "42"
numberQuoted: "42"
name: myService
spec:
ports:
- port: 7002
`
)
func TestAnnotationsTransformer(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
PrepBuiltin("AnnotationsTransformer")
defer th.Reset()
th.RunTransformerAndCheckResult(config, input, expectedOutput)
`)
}

View File

@@ -3,8 +3,7 @@ module sigs.k8s.io/kustomize/plugin/builtin/annotationstransformer
go 1.15
require (
sigs.k8s.io/kustomize/api v0.7.0
sigs.k8s.io/kustomize/kyaml v0.10.3
sigs.k8s.io/kustomize/api v0.7.1
sigs.k8s.io/yaml v1.2.0
)

View File

@@ -511,8 +511,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-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=

View File

@@ -3,7 +3,7 @@ module sigs.k8s.io/kustomize/plugin/builtin/configmapgenerator
go 1.15
require (
sigs.k8s.io/kustomize/api v0.7.0
sigs.k8s.io/kustomize/api v0.7.1
sigs.k8s.io/yaml v1.2.0
)

View File

@@ -511,8 +511,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-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=

View File

@@ -32,6 +32,7 @@ func (p *plugin) Transform(m resmap.ResMap) error {
if err != nil {
return err
}
res.SetOriginalName(res.GetName(), false)
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
}
}

View File

@@ -53,7 +53,7 @@ spec:
image: nginx:1.7.9
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
kind: ConfigMap
metadata:

View File

@@ -2,7 +2,7 @@ module sigs.k8s.io/kustomize/plugin/builtin/hashtransformer
go 1.15
require sigs.k8s.io/kustomize/api v0.7.0
require sigs.k8s.io/kustomize/api v0.7.1
replace sigs.k8s.io/kustomize/kyaml => ../../../kyaml

View File

@@ -511,8 +511,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-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=

View File

@@ -5,7 +5,7 @@ go 1.15
require (
github.com/imdario/mergo v0.3.5
github.com/pkg/errors v0.8.1
sigs.k8s.io/kustomize/api v0.7.0
sigs.k8s.io/kustomize/api v0.7.1
sigs.k8s.io/yaml v1.2.0
)

View File

@@ -506,8 +506,8 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
@@ -529,8 +529,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.10.3 h1:ARSJUMN/c3k31DYxRfZ+vp/UepUQjg9zCwny7Oj908I=
sigs.k8s.io/kustomize/kyaml v0.10.3/go.mod h1:RA+iCHA2wPCOfv6uG6TfXXWhYsHpgErq/AljxWKuxtg=
sigs.k8s.io/kustomize/kyaml v0.10.6 h1:xUJxc/k8JoWqHUahaB8DTqY0KwEPxTbTGStvW8TOcDc=
sigs.k8s.io/kustomize/kyaml v0.10.6/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

View File

@@ -12,7 +12,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/imagetag"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -36,17 +35,17 @@ func (p *plugin) Config(
func (p *plugin) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
// traverse all fields at first
err := filtersutil.ApplyToJSON(imagetag.LegacyFilter{
err := r.ApplyFilter(imagetag.LegacyFilter{
ImageTag: p.ImageTag,
}, r)
})
if err != nil {
return err
}
// then use user specified field specs
err = filtersutil.ApplyToJSON(imagetag.Filter{
err = r.ApplyFilter(imagetag.Filter{
ImageTag: p.ImageTag,
FsSlice: p.FieldSpecs,
}, r)
})
if err != nil {
return err
}

View File

@@ -52,7 +52,7 @@ spec:
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment
@@ -122,7 +122,7 @@ spec:
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment
@@ -194,7 +194,7 @@ spec:
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment
@@ -265,7 +265,7 @@ spec:
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment
@@ -337,7 +337,7 @@ spec:
name: init-alpine
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment
@@ -395,7 +395,7 @@ spec:
containers:
initContainers:
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment
@@ -438,7 +438,7 @@ spec:
name: my-image
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment
@@ -480,7 +480,7 @@ spec:
name: my-image
`)
th.AssertActualEqualsExpected(rm, `
th.AssertActualEqualsExpectedNoIdAnnotations(rm, `
apiVersion: v1
group: apps
kind: Deployment

View File

@@ -3,8 +3,7 @@ module sigs.k8s.io/kustomize/plugin/builtin/imagetagtransformer
go 1.15
require (
sigs.k8s.io/kustomize/api v0.7.0
sigs.k8s.io/kustomize/kyaml v0.10.3
sigs.k8s.io/kustomize/api v0.7.1
sigs.k8s.io/yaml v1.2.0
)

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