Add test for issue 3489 and improve error messages.

This commit is contained in:
monopole
2021-01-28 22:56:32 -08:00
parent a5cdd98414
commit 4287e28ff4
9 changed files with 368 additions and 143 deletions

View File

@@ -41,7 +41,7 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
if err := fltr.filter(obj); err != nil { if err := fltr.filter(obj); err != nil {
s, _ := obj.String() s, _ := obj.String()
return nil, errors.WrapPrefixf(err, return nil, errors.WrapPrefixf(err,
"obj '%s' at path '%v'", s, fltr.FieldSpec.Path) "considering field '%s' of object\n%v", fltr.FieldSpec.Path, s)
} }
return obj, nil return obj, nil
} }

View File

@@ -176,8 +176,11 @@ kind: Bar
a: a:
b: a b: a
`, `,
error: "obj 'kind: Bar\na:\n b: a\n' at path 'a/b/c': " + error: `considering field 'a/b/c' of object
"expected sequence or mapping node", kind: Bar
a:
b: a
: expected sequence or mapping node`,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
}, },

View File

@@ -5,12 +5,13 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/fieldspec" "sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
kyaml_filtersutil "sigs.k8s.io/kustomize/kyaml/filtersutil" "sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
@@ -38,18 +39,28 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
} }
// The node passed in here is the same node as held in Referrer, and // The node passed in here is the same node as held in Referrer;
// that's how the referrer's name field is updated. // that's how the referrer's name field is updated.
// However, this filter still needs the extra methods on Referrer // Currently, however, this filter still needs the extra methods on Referrer
// to consult things like the resource Id, its namespace, etc. // to consult things like the resource Id, its namespace, etc.
// TODO(3455): No filter should use the Resource api; all information
// about names should come from annotations, with helper methods
// on the RNode object. Resource should get stupider, RNode smarter.
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
err := node.PipeE(fieldspec.Filter{ if err := node.PipeE(fieldspec.Filter{
FieldSpec: f.NameFieldToUpdate, FieldSpec: f.NameFieldToUpdate,
SetValue: f.set, SetValue: f.set,
}) }); err != nil {
return node, err return nil, errors.Wrapf(
err, "updating name reference in '%s' field of '%s'",
f.NameFieldToUpdate.Path, f.Referrer.CurId().String())
}
return node, nil
} }
// This function is called at many nodes in the YAML doc tree.
// Only on first entry can one expect the argument to match the
// top-level node backing the Referrer.
func (f Filter) set(node *yaml.RNode) error { func (f Filter) set(node *yaml.RNode) error {
if yaml.IsMissingOrNull(node) { if yaml.IsMissingOrNull(node) {
return nil return nil
@@ -65,8 +76,7 @@ func (f Filter) set(node *yaml.RNode) error {
setMappingFn: f.setMapping, setMappingFn: f.setMapping,
}, node) }, node)
default: default:
return fmt.Errorf( return fmt.Errorf("node must be a scalar, sequence or map")
"node is expected to be either a string or a slice of string or a map of string")
} }
} }
@@ -76,16 +86,19 @@ func (f Filter) setMapping(node *yaml.RNode) error {
return fmt.Errorf("expect a mapping node") return fmt.Errorf("expect a mapping node")
} }
nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"}) nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"})
if err != nil || nameNode == nil { if err != nil {
return fmt.Errorf("cannot find field 'name' in node") return errors.Wrap(err, "trying to match 'name' field")
}
if nameNode == nil {
return fmt.Errorf("no 'name' field in node")
} }
namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"}) namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"})
if err != nil { if err != nil {
return fmt.Errorf("error when find field 'namespace'") return errors.Wrap(err, "trying to match 'namespace' field")
} }
// name will not be updated if the namespace doesn't match // name will not be updated if the namespace doesn't match
subset := f.ReferralCandidates.Resources() candidates := f.ReferralCandidates.Resources()
if namespaceNode != nil { if namespaceNode != nil {
namespace := namespaceNode.YNode().Value namespace := namespaceNode.YNode().Value
bynamespace := f.ReferralCandidates.GroupedByOriginalNamespace() bynamespace := f.ReferralCandidates.GroupedByOriginalNamespace()
@@ -95,57 +108,57 @@ func (f Filter) setMapping(node *yaml.RNode) error {
return nil return nil
} }
} }
subset = bynamespace[namespace] candidates = bynamespace[namespace]
} }
oldName := nameNode.YNode().Value oldName := nameNode.YNode().Value
res, err := f.selectReferral(oldName, subset) referral, err := f.selectReferral(oldName, candidates)
if err != nil || res == nil { if err != nil || referral == nil {
// Nil res means nothing to do. // Nil referral means nothing to do.
return err return err
} }
f.recordTheReferral(res) f.recordTheReferral(referral)
if res.GetName() == oldName && res.GetNamespace() == "" { if referral.GetName() == oldName && referral.GetNamespace() == "" {
// The name has not changed, nothing to do. // The name has not changed, nothing to do.
return nil return nil
} }
err = node.PipeE(yaml.FieldSetter{ err = node.PipeE(yaml.FieldSetter{
Name: "name", Name: "name",
StringValue: res.GetName(), StringValue: referral.GetName(),
}) })
if err != nil { if err != nil {
return err return err
} }
if res.GetNamespace() != "" { if referral.GetNamespace() != "" {
// We don't want value "" to replace value "default" since // We don't want value "" to replace value "default" since
// the empty string is handled as a wild card here not default namespace // the empty string is handled as a wild card here not default namespace
// by kubernetes. // by kubernetes.
err = node.PipeE(yaml.FieldSetter{ err = node.PipeE(yaml.FieldSetter{
Name: "namespace", Name: "namespace",
StringValue: res.GetNamespace(), StringValue: referral.GetNamespace(),
}) })
} }
return err return err
} }
func (f Filter) setScalar(node *yaml.RNode) error { func (f Filter) setScalar(node *yaml.RNode) error {
res, err := f.selectReferral( referral, err := f.selectReferral(
node.YNode().Value, f.ReferralCandidates.Resources()) node.YNode().Value, f.ReferralCandidates.Resources())
if err != nil || res == nil { if err != nil || referral == nil {
// Nil res means nothing to do. // Nil referral means nothing to do.
return err return err
} }
f.recordTheReferral(res) f.recordTheReferral(referral)
if res.GetName() == node.YNode().Value { if referral.GetName() == node.YNode().Value {
// The name has not changed, nothing to do. // The name has not changed, nothing to do.
return nil return nil
} }
return node.PipeE(yaml.FieldSetter{StringValue: res.GetName()}) return node.PipeE(yaml.FieldSetter{StringValue: referral.GetName()})
} }
// In the resource, make a note that it is referred to by the referrer. // In the resource, make a note that it is referred to by the Referrer.
func (f Filter) recordTheReferral(res *resource.Resource) { func (f Filter) recordTheReferral(referral *resource.Resource) {
res.AppendRefBy(f.Referrer.CurId()) referral.AppendRefBy(f.Referrer.CurId())
} }
func (f Filter) isRoleRef() bool { func (f Filter) isRoleRef() bool {
@@ -155,7 +168,7 @@ func (f Filter) isRoleRef() bool {
// getRoleRefGvk returns a Gvk in the roleRef field. Return error // getRoleRefGvk returns a Gvk in the roleRef field. Return error
// if the roleRef, roleRef/apiGroup or roleRef/kind is missing. // if the roleRef, roleRef/apiGroup or roleRef/kind is missing.
func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) { func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) {
n, err := kyaml_filtersutil.GetRNode(res) n, err := filtersutil.GetRNode(res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -213,7 +226,7 @@ func (f Filter) filterReferralCandidates(
// namespace. // namespace.
func (f Filter) selectReferral( func (f Filter) selectReferral(
oldName string, oldName string,
candidateSubset []*resource.Resource) (*resource.Resource, error) { referralCandidates []*resource.Resource) (*resource.Resource, error) {
var roleRefGvk *resid.Gvk var roleRefGvk *resid.Gvk
if f.isRoleRef() { if f.isRoleRef() {
var err error var err error
@@ -222,11 +235,11 @@ func (f Filter) selectReferral(
return nil, err return nil, err
} }
} }
for _, res := range candidateSubset { for _, candidate := range referralCandidates {
if res.GetOriginalName() != oldName { if candidate.GetOriginalName() != oldName {
continue continue
} }
id := res.OrgId() id := candidate.OrgId()
if !id.IsSelected(&f.ReferralTarget) { if !id.IsSelected(&f.ReferralTarget) {
continue continue
} }
@@ -242,28 +255,26 @@ func (f Filter) selectReferral(
filteredMatches := f.filterReferralCandidates(matches) filteredMatches := f.filterReferralCandidates(matches)
if len(filteredMatches) > 1 { if len(filteredMatches) > 1 {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"multiple matches for %s:\n %v", "cannot fix name in '%s' field of referrer '%s';"+
id, getIds(filteredMatches)) " found multiple possible referrals: %v",
f.NameFieldToUpdate.Path,
f.Referrer.CurId(),
getIds(filteredMatches))
} }
// Check is the match the resource we are working on // Check is the match the resource we are working on
if len(filteredMatches) == 0 || res != filteredMatches[0] { if len(filteredMatches) == 0 || candidate != filteredMatches[0] {
continue continue
} }
} }
// In the resource, note that it is referenced return candidate, nil
// by the referrer.
res.AppendRefBy(f.Referrer.CurId())
// Return transformed name of the object,
// complete with prefixes, hashes, etc.
return res, nil
} }
return nil, nil return nil, nil
} }
func getIds(rs []*resource.Resource) []string { func getIds(rs []*resource.Resource) string {
var result []string var result []string
for _, r := range rs { for _, r := range rs {
result = append(result, r.CurId().String()+"\n") result = append(result, r.CurId().String())
} }
return result return strings.Join(result, ", ")
} }

View File

@@ -14,14 +14,14 @@ import (
func TestNamerefFilter(t *testing.T) { func TestNamerefFilter(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
input string referrerOriginal string
candidates string candidates string
expected string referrerFinal string
filter Filter filter Filter
originalNames []string originalNames []string
}{ }{
"simple scalar": { "simple scalar": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -41,7 +41,7 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", ""},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -59,7 +59,7 @@ ref:
}, },
}, },
"sequence": { "sequence": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -80,7 +80,7 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName1", ""}, originalNames: []string{"oldName1", ""},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -99,7 +99,7 @@ seq:
}, },
}, },
"mapping": { "mapping": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -119,7 +119,7 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", ""},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -137,7 +137,7 @@ map:
}, },
}, },
"mapping with namespace": { "mapping with namespace": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -159,7 +159,7 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", ""},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -178,7 +178,7 @@ map:
}, },
}, },
"null value": { "null value": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -198,7 +198,7 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", ""},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -220,7 +220,7 @@ map:
for tn, tc := range testCases { for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) { t.Run(tn, func(t *testing.T) {
factory := provider.NewDefaultDepProvider().GetResourceFactory() factory := provider.NewDefaultDepProvider().GetResourceFactory()
referrer, err := factory.FromBytes([]byte(tc.input)) referrer, err := factory.FromBytes([]byte(tc.referrerOriginal))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -237,9 +237,9 @@ map:
tc.filter.ReferralCandidates = candidates tc.filter.ReferralCandidates = candidates
if !assert.Equal(t, if !assert.Equal(t,
strings.TrimSpace(tc.expected), strings.TrimSpace(tc.referrerFinal),
strings.TrimSpace( strings.TrimSpace(
filtertest_test.RunFilter(t, tc.input, tc.filter))) { filtertest_test.RunFilter(t, tc.referrerOriginal, tc.filter))) {
t.FailNow() t.FailNow()
} }
}) })
@@ -248,14 +248,14 @@ map:
func TestNamerefFilterUnhappy(t *testing.T) { func TestNamerefFilterUnhappy(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
input string referrerOriginal string
candidates string candidates string
expected string referrerFinal string
filter Filter filter Filter
originalNames []string originalNames []string
}{ }{
"multiple match": { "multiple match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -275,7 +275,7 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", "oldName"}, originalNames: []string{"oldName", "oldName"},
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{ ReferralTarget: resid.Gvk{
@@ -286,7 +286,7 @@ metadata:
}, },
}, },
"no name": { "no name": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -306,7 +306,7 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", "oldName"}, originalNames: []string{"oldName", "oldName"},
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
NameFieldToUpdate: types.FieldSpec{Path: "ref"}, NameFieldToUpdate: types.FieldSpec{Path: "ref"},
ReferralTarget: resid.Gvk{ ReferralTarget: resid.Gvk{
@@ -321,7 +321,7 @@ metadata:
for tn, tc := range testCases { for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) { t.Run(tn, func(t *testing.T) {
factory := provider.NewDefaultDepProvider().GetResourceFactory() factory := provider.NewDefaultDepProvider().GetResourceFactory()
referrer, err := factory.FromBytes([]byte(tc.input)) referrer, err := factory.FromBytes([]byte(tc.referrerOriginal))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -337,11 +337,11 @@ metadata:
candidates := resMapFactory.FromResourceSlice(candidatesRes) candidates := resMapFactory.FromResourceSlice(candidatesRes)
tc.filter.ReferralCandidates = candidates tc.filter.ReferralCandidates = candidates
_, err = filtertest_test.RunFilterE(t, tc.input, tc.filter) _, err = filtertest_test.RunFilterE(t, tc.referrerOriginal, tc.filter)
if err == nil { if err == nil {
t.Fatalf("expect an error") t.Fatalf("expect an error")
} }
if tc.expected != "" && !assert.EqualError(t, err, tc.expected) { if tc.referrerFinal != "" && !assert.EqualError(t, err, tc.referrerFinal) {
t.FailNow() t.FailNow()
} }
}) })
@@ -350,19 +350,19 @@ metadata:
func TestCandidatesWithDifferentPrefixSuffix(t *testing.T) { func TestCandidatesWithDifferentPrefixSuffix(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
input string referrerOriginal string
candidates string candidates string
expected string referrerFinal string
filter Filter filter Filter
originalNames []string originalNames []string
prefix []string prefix []string
suffix []string suffix []string
inputPrefix string inputPrefix string
inputSuffix string inputSuffix string
err bool err bool
}{ }{
"prefix match": { "prefix match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -386,7 +386,7 @@ metadata:
suffix: []string{"", "suffix2"}, suffix: []string{"", "suffix2"},
inputPrefix: "prefix1", inputPrefix: "prefix1",
inputSuffix: "", inputSuffix: "",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -405,7 +405,7 @@ ref:
err: false, err: false,
}, },
"suffix match": { "suffix match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -429,7 +429,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "", inputPrefix: "",
inputSuffix: "suffix1", inputSuffix: "suffix1",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -448,7 +448,7 @@ ref:
err: false, err: false,
}, },
"prefix suffix both match": { "prefix suffix both match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -472,7 +472,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "prefix1", inputPrefix: "prefix1",
inputSuffix: "suffix1", inputSuffix: "suffix1",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -491,7 +491,7 @@ ref:
err: false, err: false,
}, },
"multiple match: both": { "multiple match: both": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -515,7 +515,7 @@ metadata:
suffix: []string{"suffix", "suffix"}, suffix: []string{"suffix", "suffix"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{ ReferralTarget: resid.Gvk{
@@ -527,7 +527,7 @@ metadata:
err: true, err: true,
}, },
"multiple match: only prefix": { "multiple match: only prefix": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -551,7 +551,7 @@ metadata:
suffix: []string{"", ""}, suffix: []string{"", ""},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "", inputSuffix: "",
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{ ReferralTarget: resid.Gvk{
@@ -563,7 +563,7 @@ metadata:
err: true, err: true,
}, },
"multiple match: only suffix": { "multiple match: only suffix": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -587,7 +587,7 @@ metadata:
suffix: []string{"suffix", "suffix"}, suffix: []string{"suffix", "suffix"},
inputPrefix: "", inputPrefix: "",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
NameFieldToUpdate: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
ReferralTarget: resid.Gvk{ ReferralTarget: resid.Gvk{
@@ -599,7 +599,7 @@ metadata:
err: true, err: true,
}, },
"no match: neither match": { "no match: neither match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -623,7 +623,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -642,7 +642,7 @@ ref:
err: false, err: false,
}, },
"no match: prefix doesn't match": { "no match: prefix doesn't match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -666,7 +666,7 @@ metadata:
suffix: []string{"suffix", "suffix"}, suffix: []string{"suffix", "suffix"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -685,7 +685,7 @@ ref:
err: false, err: false,
}, },
"no match: suffix doesn't match": { "no match: suffix doesn't match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -709,7 +709,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -732,7 +732,7 @@ ref:
for tn, tc := range testCases { for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) { t.Run(tn, func(t *testing.T) {
factory := provider.NewDefaultDepProvider().GetResourceFactory() factory := provider.NewDefaultDepProvider().GetResourceFactory()
referrer, err := factory.FromBytes([]byte(tc.input)) referrer, err := factory.FromBytes([]byte(tc.referrerOriginal))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -764,13 +764,15 @@ ref:
if !tc.err { if !tc.err {
if !assert.Equal(t, if !assert.Equal(t,
strings.TrimSpace(tc.expected), strings.TrimSpace(tc.referrerFinal),
strings.TrimSpace( strings.TrimSpace(
filtertest_test.RunFilter(t, tc.input, tc.filter))) { filtertest_test.RunFilter(
t, tc.referrerOriginal, tc.filter))) {
t.FailNow() t.FailNow()
} }
} else { } else {
_, err := filtertest_test.RunFilterE(t, tc.input, tc.filter) _, err := filtertest_test.RunFilterE(
t, tc.referrerOriginal, tc.filter)
if err == nil { if err == nil {
t.Fatalf("an error is expected") t.Fatalf("an error is expected")
} }

View File

@@ -243,7 +243,8 @@ metadata:
data: data:
slice: slice:
- false`, - false`,
expectedError: `obj 'apiVersion: apps/v1 expectedError: `considering field 'data/slice' of object
apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: dep name: dep
@@ -252,7 +253,7 @@ metadata:
data: data:
slice: slice:
- false - false
' at path 'data/slice': invalid value type expect a string`, : invalid value type expect a string`,
filter: Filter{ filter: Filter{
MappingFunc: makeMf(map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5), "VAR": int64(5),
@@ -268,7 +269,8 @@ metadata:
name: dep name: dep
data: data:
1: str`, 1: str`,
expectedError: `obj 'apiVersion: apps/v1 expectedError: `considering field 'data' of object
apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: dep name: dep
@@ -276,7 +278,7 @@ metadata:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
data: data:
1: str 1: str
' at path 'data': invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`, : invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`,
filter: Filter{ filter: Filter{
MappingFunc: makeMf(map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5), "VAR": int64(5),

View File

@@ -7,6 +7,8 @@ import (
"strings" "strings"
"testing" "testing"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/provider" "sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
@@ -518,7 +520,27 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
}, },
}, },
}).ResMap(), }).ResMap(),
expectedErr: "cannot find field 'name' in node"}, // TODO(#3304): DECISION - kyaml better; not a bug.
expectedErr: konfig.IfApiMachineryElseKyaml(
`updating name reference in 'rules/resourceNames' field of `+
`'rbac.authorization.k8s.io_v1_ClusterRole|~X|cr'`+
`: considering field 'rules/resourceNames' of object
{"apiVersion": "rbac.authorization.k8s.io/v1", "kind": "ClusterRole", "metadata": {
"name": "cr"}, "rules": [{"resourceNames": {"foo": "bar"}, "resources": ["secrets"]}]}
: visit traversal on path: [resourceNames]: no 'name' field in node`,
`updating name reference in 'rules/resourceNames' field of `+
`'rbac.authorization.k8s.io_v1_ClusterRole|~X|cr'`+
`: considering field 'rules/resourceNames' of object
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cr
rules:
- resourceNames:
foo: bar
resources:
- secrets
: visit traversal on path: [resourceNames]: no 'name' field in node`)},
} }
nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
@@ -529,7 +551,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
} }
if !strings.Contains(err.Error(), test.expectedErr) { if !strings.Contains(err.Error(), test.expectedErr) {
t.Fatalf("Incorrect error.\nExpected: %s, but got %v", t.Fatalf("Incorrect error.\nExpected:\n %s\nGot:\n%v",
test.expectedErr, err) test.expectedErr, err)
} }
} }

View File

@@ -24,14 +24,12 @@ func TestRefVarTransformer(t *testing.T) {
res resmap.ResMap res resmap.ResMap
unused []string unused []string
} }
testCases := []struct { testCases := map[string]struct {
description string given given
given given expected expected
expected expected errMessage string
errMessage string
}{ }{
{ "var replacement in map[string]": {
description: "var replacement in map[string]",
given: given{ given: given{
varMap: map[string]interface{}{ varMap: map[string]interface{}{
"FOO": "replacementForFoo", "FOO": "replacementForFoo",
@@ -106,8 +104,7 @@ func TestRefVarTransformer(t *testing.T) {
unused: []string{"BAR"}, unused: []string{"BAR"},
}, },
}, },
{ "var replacement panic in map[string]": {
description: "var replacement panic in map[string]",
given: given{ given: given{
varMap: map[string]interface{}{}, varMap: map[string]interface{}{},
fs: []types.FieldSpec{ fs: []types.FieldSpec{
@@ -126,20 +123,21 @@ func TestRefVarTransformer(t *testing.T) {
}, },
// TODO(#3304): DECISION - kyaml better; not a bug. // TODO(#3304): DECISION - kyaml better; not a bug.
errMessage: konfig.IfApiMachineryElseKyaml( errMessage: konfig.IfApiMachineryElseKyaml(
`obj '{"apiVersion": "v1", "data": {"slice": [5]}, "kind": "ConfigMap", "metadata": {"name": "cm1"}} `considering field 'data/slice' of object
' at path 'data/slice': invalid value type expect a string`, {"apiVersion": "v1", "data": {"slice": [5]}, "kind": "ConfigMap", "metadata": {"name": "cm1"}}
`obj 'apiVersion: v1 : invalid value type expect a string`,
`considering field 'data/slice' of object
apiVersion: v1
data: data:
slice: slice:
- 5 - 5
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: cm1 name: cm1
' at path 'data/slice': invalid value type expect a string`, : invalid value type expect a string`,
), ),
}, },
{ "var replacement in nil": {
description: "var replacement in nil",
given: given{ given: given{
varMap: map[string]interface{}{}, varMap: map[string]interface{}{},
fs: []types.FieldSpec{ fs: []types.FieldSpec{
@@ -171,20 +169,18 @@ metadata:
}, },
} }
for _, tc := range testCases { for tn, tc := range testCases {
t.Run(tc.description, func(t *testing.T) { t.Run(tn, func(t *testing.T) {
// arrange
tr := newRefVarTransformer(tc.given.varMap, tc.given.fs) tr := newRefVarTransformer(tc.given.varMap, tc.given.fs)
// act
err := tr.Transform(tc.given.res) err := tr.Transform(tc.given.res)
// assert
if tc.errMessage != "" { if tc.errMessage != "" {
if err == nil { if err == nil {
t.Fatalf("missing expected error %v", tc.errMessage) t.Fatalf("missing expected error %v", tc.errMessage)
} else if err.Error() != tc.errMessage { } else if err.Error() != tc.errMessage {
t.Fatalf("actual error doesn't match expected error: \nACTUAL: %v\nEXPECTED: %v", err.Error(), tc.errMessage) t.Fatalf(`actual error doesn't match expected error:
ACTUAL: %v
EXPECTED: %v`,
err.Error(), tc.errMessage)
} }
} else { } else {
if err != nil { if err != nil {
@@ -194,7 +190,13 @@ metadata:
a, e := tc.given.res, tc.expected.res a, e := tc.given.res, tc.expected.res
if !reflect.DeepEqual(a, e) { if !reflect.DeepEqual(a, e) {
err = e.ErrorIfNotEqualLists(a) err = e.ErrorIfNotEqualLists(a)
t.Fatalf("actual doesn't match expected: \nACTUAL:\n%v\nEXPECTED:\n%v\nERR: %v", a, e, err) t.Fatalf(`actual doesn't match expected:
ACTUAL:
%v
EXPECTED:
%v
ERR: %v`,
a, e, err)
} }
} }
}) })

View File

@@ -1,11 +1,194 @@
package krusty_test package krusty_test
import ( import (
"strings"
"testing" "testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
) )
func TestIssue3489(t *testing.T) {
const assets = `{
"tenantId": "XXXXX-XXXXXX-XXXXX-XXXXXX-XXXXXX",
"subscriptionId": "XXXXX-XXXXXX-XXXXX-XXXXXX-XXXXXX",
"resourceGroup": "DNS-EUW-XXX-RG",
"useManagedIdentityExtension": true,
"userAssignedIdentityID": "XXXXX-XXXXXX-XXXXX-XXXXXX-XXXXXX"
}
`
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
namespace: kube-system
resources:
- external-dns
- external-dns-private
`)
th.WriteK("external-dns", `
resources:
- ../base
commonLabels:
app: external-dns
instance: public
images:
- name: k8s.gcr.io/external-dns/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
- name: quay.io/sylr/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
secretGenerator:
- name: azure-config-file
behavior: replace
files:
- assets/azure.json
patches:
- target:
group: apps
version: v1
kind: Deployment
name: external-dns
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args
value:
- --txt-owner-id="aks"
- --txt-prefix=external-dns-
- --source=service
- --provider=azure
- --registry=txt
- --domain-filter=dev.company.com
`)
th.WriteF("external-dns/assets/azure.json", assets)
th.WriteK("external-dns-private", `
resources:
- ../base
nameSuffix: -private
commonLabels:
app: external-dns
instance: private
images:
- name: k8s.gcr.io/external-dns/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
- name: quay.io/sylr/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
secretGenerator:
- name: azure-config-file
behavior: replace
files:
- assets/azure.json
patches:
- target:
group: apps
version: v1
kind: Deployment
name: external-dns
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args
value:
- --txt-owner-id="aks"
- --txt-prefix=external-dns-private-
- --source=service
- --provider=azure-private-dns
- --registry=txt
- --domain-filter=static.company.az
`)
th.WriteF("external-dns-private/assets/azure.json", assets)
th.WriteK("base", `
resources:
- clusterrole.yaml
- clusterrolebinding.yaml
- deployment.yaml
- serviceaccount.yaml
commonLabels:
app: external-dns
instance: public
images:
- name: k8s.gcr.io/external-dns/external-dns
newName: quay.io/sylr/external-dns
newTag: v0.7.4-73-g00a9a0c7
secretGenerator:
- name: azure-config-file
files:
- assets/azure.json
`)
th.WriteF("base/assets/azure.json", assets)
th.WriteF("base/clusterrolebinding.yaml", `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
`)
th.WriteF("base/clusterrole.yaml", `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: ['']
resources: ['endpoints', 'pods', 'services', 'nodes']
verbs: ['get', 'watch', 'list']
- apiGroups: ['extensions', 'networking.k8s.io']
resources: ['ingresses']
verbs: ['get', 'watch', 'list']
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels: {}
template:
metadata: {}
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: k8s.gcr.io/external-dns/external-dns
args:
- --domain-filter=""
- --txt-owner-id=""
- --txt-prefix=external-dns-
- --source=service
- --provider=azure
- --registry=txt
resources: {}
volumeMounts:
- name: azure-config-file
mountPath: /etc/kubernetes
readOnly: true
volumes:
- name: azure-config-file
secret:
secretName: azure-config-file
`)
th.WriteF("base/serviceaccount.yaml", `
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
`)
// TODO(3489): This shouldn't be an error.
err := th.RunWithErr(".", th.MakeDefaultOptions())
if !strings.Contains(err.Error(), "found multiple possible referrals") {
t.Fatalf("unexpected error: %q", err)
}
}
func TestEmptyFieldSpecValue(t *testing.T) { func TestEmptyFieldSpecValue(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteK("/app", ` th.WriteK("/app", `

View File

@@ -362,7 +362,7 @@ metadata:
if err == nil { if err == nil {
t.Fatalf("expected error") t.Fatalf("expected error")
} }
if !strings.Contains(err.Error(), "multiple matches for ~G_v1_ServiceAccount|~X|serviceaccount") { if !strings.Contains(err.Error(), "found multiple possible referrals") {
t.Fatalf("unexpected error %v", err) t.Fatalf("unexpected error %v", err)
} }
} }