Add var ref replacement tests and more doc.

This commit is contained in:
monopole
2021-01-10 09:16:52 -08:00
parent 1a2779b2c3
commit 30dcf38609
5 changed files with 144 additions and 59 deletions

View File

@@ -6,7 +6,6 @@ import (
"strings" "strings"
"sigs.k8s.io/kustomize/api/filters/fieldspec" "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/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"
@@ -33,6 +32,8 @@ type Filter struct {
ReferralCandidates resmap.ResMap ReferralCandidates resmap.ResMap
} }
// 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) { 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)
} }
@@ -40,7 +41,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
// 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, and
// 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 // However, this filter still needs the extra methods on Referrer
// to consult things like the resource Id, it's namespace, etc. // to consult things like the resource Id, its namespace, etc.
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{ err := node.PipeE(fieldspec.Filter{
FieldSpec: f.NameFieldToUpdate, FieldSpec: f.NameFieldToUpdate,
@@ -95,41 +96,53 @@ func (f Filter) setMapping(node *yaml.RNode) error {
} }
oldName := nameNode.YNode().Value oldName := nameNode.YNode().Value
newName, newNamespace, err := f.selectReferral(oldName, subset) 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 { if err != nil {
return err return err
} }
if res.GetNamespace() != "" {
if newName == oldName && newNamespace == "" {
// Nothing to do.
return nil
}
// set name
node.Pipe(yaml.FieldSetter{
Name: "name",
StringValue: newName,
})
if newNamespace != "" {
// 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.
node.Pipe(yaml.FieldSetter{ err = node.PipeE(yaml.FieldSetter{
Name: "namespace", Name: "namespace",
StringValue: newNamespace, StringValue: res.GetNamespace(),
}) })
} }
return nil return err
} }
func (f Filter) setScalar(node *yaml.RNode) error { func (f Filter) setScalar(node *yaml.RNode) error {
newValue, _, err := f.selectReferral( res, err := f.selectReferral(
node.YNode().Value, node.YNode().Value, f.ReferralCandidates.Resources())
f.ReferralCandidates.Resources()) if err != nil || res == nil {
if err != nil { // Nil res means nothing to do.
return err return err
} }
return filtersutil.SetScalar(newValue)(node) f.recordTheReferral(res)
if res.GetName() == node.YNode().Value {
// The name has not changed, nothing to do.
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 { func (f Filter) isRoleRef() bool {
@@ -191,35 +204,41 @@ func (f Filter) filterReferralCandidates(
} }
// selectReferral picks the referral among a subset of candidates. // selectReferral picks the referral among a subset of candidates.
// It returns the current name and namespace of the selected candidate. // The content of the candidateSubset slice is most of the time
// The content of the referricalCandidateSubset slice is most of the time // identical to the ReferralCandidates resmap. Still in some cases, such
// identical to the referralCandidates resmap. Still in some cases, such
// as ClusterRoleBinding, the subset only contains the resources of a specific // as ClusterRoleBinding, the subset only contains the resources of a specific
// namespace. // namespace.
func (f Filter) selectReferral( func (f Filter) selectReferral(
oldName string, oldName string,
referralCandidateSubset []*resource.Resource) (string, string, error) { candidateSubset []*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
roleRefGvk, err = getRoleRefGvk(f.Referrer) roleRefGvk, err = getRoleRefGvk(f.Referrer)
if err != nil { if err != nil {
return "", "", err return nil, err
} }
} }
for _, res := range referralCandidateSubset { for _, res := range candidateSubset {
if res.GetOriginalName() != oldName {
continue
}
id := res.OrgId() id := res.OrgId()
if !id.IsSelected(&f.ReferralTarget) {
continue
}
// If the we are processing a roleRef, the apiGroup and Kind in the // If the we are processing a roleRef, the apiGroup and Kind in the
// roleRef are needed to be considered. // roleRef are needed to be considered.
if (!f.isRoleRef() || id.IsSelected(roleRefGvk)) && if f.isRoleRef() && !id.IsSelected(roleRefGvk) {
id.IsSelected(&f.ReferralTarget) && res.GetOriginalName() == oldName { continue
}
matches := f.ReferralCandidates.GetMatchingResourcesByOriginalId(id.Equals) matches := f.ReferralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
// If there's more than one match, // If there's more than one match,
// filter the matches by prefix and suffix // filter the matches by prefix and suffix
if len(matches) > 1 { if len(matches) > 1 {
filteredMatches := f.filterReferralCandidates(matches) filteredMatches := f.filterReferralCandidates(matches)
if len(filteredMatches) > 1 { if len(filteredMatches) > 1 {
return "", "", fmt.Errorf( return nil, fmt.Errorf(
"multiple matches for %s:\n %v", "multiple matches for %s:\n %v",
id, getIds(filteredMatches)) id, getIds(filteredMatches))
} }
@@ -233,10 +252,9 @@ func (f Filter) selectReferral(
res.AppendRefBy(f.Referrer.CurId()) res.AppendRefBy(f.Referrer.CurId())
// Return transformed name of the object, // Return transformed name of the object,
// complete with prefixes, hashes, etc. // complete with prefixes, hashes, etc.
return res.GetName(), res.GetNamespace(), nil return res, nil
} }
} return nil, nil
return oldName, "", nil
} }
func getIds(rs []*resource.Resource) []string { func getIds(rs []*resource.Resource) []string {

View File

@@ -19,7 +19,8 @@ var _ resmap.Transformer = &nameReferenceTransformer{}
// newNameReferenceTransformer constructs a nameReferenceTransformer // newNameReferenceTransformer constructs a nameReferenceTransformer
// with a given slice of NameBackReferences. // with a given slice of NameBackReferences.
func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.Transformer { func newNameReferenceTransformer(
br []builtinconfig.NameBackReferences) resmap.Transformer {
if br == nil { if br == nil {
log.Fatal("backrefs not expected to be nil") log.Fatal("backrefs not expected to be nil")
} }
@@ -81,8 +82,18 @@ func (t *nameReferenceTransformer) Transform(m resmap.ResMap) error {
for _, fSpec := range referralTarget.FieldSpecs { for _, fSpec := range referralTarget.FieldSpecs {
if referrer.OrgId().IsSelected(&fSpec.Gvk) { if referrer.OrgId().IsSelected(&fSpec.Gvk) {
if candidates == nil { 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) candidates = m.SubsetThatCouldBeReferencedByResource(referrer)
} }
// 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{ err := referrer.ApplyFilter(nameref.Filter{
Referrer: referrer, Referrer: referrer,
NameFieldToUpdate: fSpec, NameFieldToUpdate: fSpec,

View File

@@ -4,9 +4,8 @@
package accumulator package accumulator
import ( import (
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
"sigs.k8s.io/kustomize/api/filters/refvar" "sigs.k8s.io/kustomize/api/filters/refvar"
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )

View File

@@ -164,6 +164,6 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
if ra.tConfig.NameReference == nil { if ra.tConfig.NameReference == nil {
return nil return nil
} }
return ra.Transform(newNameReferenceTransformer( return ra.Transform(
ra.tConfig.NameReference)) newNameReferenceTransformer(ra.tConfig.NameReference))
} }

View File

@@ -929,7 +929,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 := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", ` th.WriteK("/app/base", `
resources: resources: