1+ package namereference
2+
3+ import (
4+ "fmt"
5+
6+ "github.com/pkg/errors"
7+ "sigs.k8s.io/kustomize/api/filters/fieldspec"
8+ "sigs.k8s.io/kustomize/api/resmap"
9+ "sigs.k8s.io/kustomize/api/resource"
10+ "sigs.k8s.io/kustomize/api/types"
11+ "sigs.k8s.io/kustomize/kyaml/kio"
12+ "sigs.k8s.io/kustomize/kyaml/resid"
13+ "sigs.k8s.io/kustomize/kyaml/yaml"
14+ )
15+
16+ type UnlimitedNameRefFilter struct {
17+ Referrer * resource.Resource
18+ NameFieldToUpdate types.FieldSpec
19+ ReferralTarget resid.Gvk
20+ ReferralCandidates resmap.ResMap
21+
22+ }
23+
24+ func (f UnlimitedNameRefFilter ) Filter (nodes []* yaml.RNode ) ([]* yaml.RNode , error ) {
25+ return kio .FilterAll (yaml .FilterFunc (f .run )).Filter (nodes )
26+ }
27+
28+ func (f UnlimitedNameRefFilter ) run (node * yaml.RNode ) (* yaml.RNode , error ) {
29+ if node .GetNamespace () != f .Referrer .GetNamespace () {
30+ return nil , fmt .Errorf ("not in the same namespace" )
31+ }
32+ if err := node .PipeE (fieldspec.Filter {
33+ FieldSpec : f .NameFieldToUpdate ,
34+ SetValue : f .updateName ,
35+ }); err != nil {
36+ return nil , errors .Wrapf (
37+ err , "updating name reference in '%s' field of '%s'" ,
38+ f .NameFieldToUpdate .Path , f .Referrer .CurId ().String ())
39+ }
40+ return node , nil
41+ }
42+
43+ func (f UnlimitedNameRefFilter ) updateName (node * yaml.RNode ) error {
44+ if yaml .IsMissingOrNull (node ) {
45+ return nil
46+ }
47+ candidates := f .ReferralCandidates .Resources ()
48+ candidates = doSieve (candidates , previousIdSelectedByGvk (& f .ReferralTarget ))
49+ candidates = doSieve (candidates , f .sameCurrentNamespaceAsReferrer ())
50+ if len (candidates ) == 0 {
51+ return nil
52+ }
53+ referral := candidates [0 ]
54+ return node .PipeE (yaml.FieldSetter {StringValue : referral .GetName ()})
55+ }
56+
57+ type sieveFunc func (* resource.Resource ) bool
58+
59+ func doSieve (list []* resource.Resource , fn sieveFunc ) (s []* resource.Resource ) {
60+ for _ , r := range list {
61+ if fn (r ) {
62+ s = append (s , r )
63+ }
64+ }
65+ return
66+ }
67+
68+ func previousIdSelectedByGvk (gvk * resid.Gvk ) sieveFunc {
69+ return func (r * resource.Resource ) bool {
70+ if r .OrgId ().IsSelected (gvk ) {
71+ return true
72+ }
73+ return false
74+ }
75+ }
76+
77+ func (f UnlimitedNameRefFilter ) sameCurrentNamespaceAsReferrer () sieveFunc {
78+ referrerCurId := f .Referrer .CurId ()
79+ if referrerCurId .IsClusterScoped () {
80+ // If the referrer is cluster-scoped, let anything through.
81+ return func (_ * resource.Resource ) bool {return true }
82+ }
83+ return func (r * resource.Resource ) bool {
84+ if r .CurId ().IsClusterScoped () {
85+ // Allow cluster-scoped through.
86+ return true
87+ }
88+ if r .GetKind () == "ServiceAccount" {
89+ // Allow service accounts through, even though they
90+ // are in a namespace. A RoleBinding in another namespace
91+ // can reference them.
92+ return true
93+ }
94+ return referrerCurId .IsNsEquals (r .CurId ())
95+ }
96+ }
0 commit comments