mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 01:14:22 +00:00
Add var ref replacement tests and more doc.
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user