1
2
3
4 package namespace
5
6 import (
7 "sigs.k8s.io/kustomize/api/filters/filtersutil"
8 "sigs.k8s.io/kustomize/api/filters/fsslice"
9 "sigs.k8s.io/kustomize/api/types"
10 "sigs.k8s.io/kustomize/kyaml/errors"
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 Filter struct {
17
18 Namespace string `yaml:"namespace,omitempty"`
19
20
21 FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
22
23
24 UnsetOnly bool `json:"unsetOnly" yaml:"unsetOnly"`
25
26
27
28
29
30
31 SetRoleBindingSubjects RoleBindingSubjectMode `json:"setRoleBindingSubjects" yaml:"setRoleBindingSubjects"`
32
33 trackableSetter filtersutil.TrackableSetter
34 }
35
36 type RoleBindingSubjectMode string
37
38 const (
39 DefaultSubjectsOnly RoleBindingSubjectMode = "defaultOnly"
40 SubjectModeUnspecified RoleBindingSubjectMode = ""
41 AllServiceAccountSubjects RoleBindingSubjectMode = "allServiceAccounts"
42 NoSubjects RoleBindingSubjectMode = "none"
43 )
44
45 var _ kio.Filter = Filter{}
46 var _ kio.TrackableFilter = &Filter{}
47
48
49 func (ns *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
50 ns.trackableSetter.WithMutationTracker(callback)
51 }
52
53 func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
54 return kio.FilterAll(yaml.FilterFunc(ns.run)).Filter(nodes)
55 }
56
57
58 func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
59
60
61
62 apiVersion := node.GetApiVersion()
63 ns.FsSlice = ns.removeUnneededMetaFieldSpecs(apiVersion, ns.FsSlice)
64 gvk := resid.GvkFromNode(node)
65 if err := ns.metaNamespaceHack(node, gvk); err != nil {
66 return nil, err
67 }
68
69
70 if isRoleBinding(gvk.Kind) {
71 ns.FsSlice = ns.removeRoleBindingSubjectFieldSpecs(ns.FsSlice)
72 if err := ns.roleBindingHack(node); err != nil {
73 return nil, err
74 }
75 }
76
77
78 err := node.PipeE(fsslice.Filter{
79 FsSlice: ns.FsSlice,
80 SetValue: ns.fieldSetter(),
81 CreateKind: yaml.ScalarNode,
82 CreateTag: yaml.NodeTagString,
83 })
84 invalidKindErr := &yaml.InvalidNodeKindError{}
85 if err != nil && errors.As(err, &invalidKindErr) && invalidKindErr.ActualNodeKind() != yaml.ScalarNode {
86 return nil, errors.WrapPrefixf(err, "namespace field specs must target scalar nodes")
87 }
88 return node, errors.WrapPrefixf(err, "namespace transformation failed")
89 }
90
91
92
93 func (ns Filter) metaNamespaceHack(obj *yaml.RNode, gvk resid.Gvk) error {
94 if gvk.IsClusterScoped() {
95 return nil
96 }
97 f := fsslice.Filter{
98 FsSlice: []types.FieldSpec{
99 {Path: types.MetadataNamespacePath, CreateIfNotPresent: true},
100 },
101 SetValue: ns.fieldSetter(),
102 CreateKind: yaml.ScalarNode,
103 }
104 _, err := f.Filter(obj)
105 return err
106 }
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126 func (ns Filter) roleBindingHack(obj *yaml.RNode) error {
127 var visitor filtersutil.SetFn
128 switch ns.SetRoleBindingSubjects {
129 case NoSubjects:
130 return nil
131 case DefaultSubjectsOnly, SubjectModeUnspecified:
132 visitor = ns.setSubjectsNamedDefault
133 case AllServiceAccountSubjects:
134 visitor = ns.setServiceAccountNamespaces
135 default:
136 return errors.Errorf("invalid value %q for setRoleBindingSubjects: "+
137 "must be one of %q, %q or %q", ns.SetRoleBindingSubjects,
138 DefaultSubjectsOnly, NoSubjects, AllServiceAccountSubjects)
139 }
140
141
142 obj, err := obj.Pipe(yaml.Lookup(subjectsField))
143 if err != nil || yaml.IsMissingOrNull(obj) {
144 return err
145 }
146
147 return errors.WrapPrefixf(obj.VisitElements(visitor), "setting namespace on (cluster)role binding subjects")
148 }
149
150 func isRoleBinding(kind string) bool {
151 return kind == roleBindingKind || kind == clusterRoleBindingKind
152 }
153
154 func (ns Filter) setServiceAccountNamespaces(o *yaml.RNode) error {
155 name, err := o.Pipe(yaml.Lookup("kind"), yaml.Match("ServiceAccount"))
156 if err != nil || yaml.IsMissingOrNull(name) {
157 return errors.WrapPrefixf(err, "looking up kind on (cluster)role binding subject")
158 }
159 return setNamespaceField(o, ns.fieldSetter())
160 }
161
162 func (ns Filter) setSubjectsNamedDefault(o *yaml.RNode) error {
163 name, err := o.Pipe(yaml.Lookup("name"), yaml.Match("default"))
164 if err != nil || yaml.IsMissingOrNull(name) {
165 return errors.WrapPrefixf(err, "looking up name on (cluster)role binding subject")
166 }
167 return setNamespaceField(o, ns.fieldSetter())
168 }
169
170 func setNamespaceField(node *yaml.RNode, setter filtersutil.SetFn) error {
171 node, err := node.Pipe(yaml.LookupCreate(yaml.ScalarNode, "namespace"))
172 if err != nil {
173 return errors.WrapPrefixf(err, "setting namespace field on (cluster)role binding subject")
174 }
175 return setter(node)
176 }
177
178
179
180 func (ns Filter) removeRoleBindingSubjectFieldSpecs(fs types.FsSlice) types.FsSlice {
181 var val types.FsSlice
182 for i := range fs {
183 if isRoleBinding(fs[i].Kind) && fs[i].Path == subjectsNamespacePath {
184 continue
185 }
186 val = append(val, fs[i])
187 }
188 return val
189 }
190
191 func (ns Filter) removeUnneededMetaFieldSpecs(apiVersion string, fs types.FsSlice) types.FsSlice {
192 var val types.FsSlice
193 for i := range fs {
194 if fs[i].Path == types.MetadataNamespacePath {
195 continue
196 }
197 if apiVersion != types.MetadataNamespaceApiVersion && fs[i].Path == types.MetadataNamePath {
198 continue
199 }
200 val = append(val, fs[i])
201 }
202 return val
203 }
204
205 func (ns *Filter) fieldSetter() filtersutil.SetFn {
206 if ns.UnsetOnly {
207 return ns.trackableSetter.SetEntryIfEmpty("", ns.Namespace, yaml.NodeTagString)
208 }
209 return ns.trackableSetter.SetEntry("", ns.Namespace, yaml.NodeTagString)
210 }
211
212 const (
213 subjectsField = "subjects"
214 subjectsNamespacePath = "subjects/namespace"
215 roleBindingKind = "RoleBinding"
216 clusterRoleBindingKind = "ClusterRoleBinding"
217 )
218
View as plain text