1
16
17 package restmapper
18
19 import (
20 "fmt"
21 "strings"
22 "sync"
23
24 "k8s.io/apimachinery/pkg/api/meta"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/runtime/schema"
27 "k8s.io/client-go/discovery"
28
29 "k8s.io/klog/v2"
30 )
31
32
33
34 type APIGroupResources struct {
35 Group metav1.APIGroup
36
37
38 VersionedResources map[string][]metav1.APIResource
39 }
40
41
42
43 func NewDiscoveryRESTMapper(groupResources []*APIGroupResources) meta.RESTMapper {
44 unionMapper := meta.MultiRESTMapper{}
45
46 var groupPriority []string
47
48 resourcePriority := []schema.GroupVersionResource{{Group: "", Version: "v1", Resource: meta.AnyResource}}
49 kindPriority := []schema.GroupVersionKind{{Group: "", Version: "v1", Kind: meta.AnyKind}}
50
51 for _, group := range groupResources {
52 groupPriority = append(groupPriority, group.Group.Name)
53
54
55 if len(group.Group.PreferredVersion.Version) != 0 {
56 preferred := group.Group.PreferredVersion.Version
57 if _, ok := group.VersionedResources[preferred]; ok {
58 resourcePriority = append(resourcePriority, schema.GroupVersionResource{
59 Group: group.Group.Name,
60 Version: group.Group.PreferredVersion.Version,
61 Resource: meta.AnyResource,
62 })
63
64 kindPriority = append(kindPriority, schema.GroupVersionKind{
65 Group: group.Group.Name,
66 Version: group.Group.PreferredVersion.Version,
67 Kind: meta.AnyKind,
68 })
69 }
70 }
71
72 for _, discoveryVersion := range group.Group.Versions {
73 resources, ok := group.VersionedResources[discoveryVersion.Version]
74 if !ok {
75 continue
76 }
77
78
79 if discoveryVersion.Version != group.Group.PreferredVersion.Version {
80 resourcePriority = append(resourcePriority, schema.GroupVersionResource{
81 Group: group.Group.Name,
82 Version: discoveryVersion.Version,
83 Resource: meta.AnyResource,
84 })
85
86 kindPriority = append(kindPriority, schema.GroupVersionKind{
87 Group: group.Group.Name,
88 Version: discoveryVersion.Version,
89 Kind: meta.AnyKind,
90 })
91 }
92
93 gv := schema.GroupVersion{Group: group.Group.Name, Version: discoveryVersion.Version}
94 versionMapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{gv})
95
96 for _, resource := range resources {
97 scope := meta.RESTScopeNamespace
98 if !resource.Namespaced {
99 scope = meta.RESTScopeRoot
100 }
101
102
103 if strings.Contains(resource.Name, "/") {
104 continue
105 }
106
107 plural := gv.WithResource(resource.Name)
108 singular := gv.WithResource(resource.SingularName)
109
110 if len(resource.SingularName) == 0 {
111 _, singular = meta.UnsafeGuessKindToResource(gv.WithKind(resource.Kind))
112 }
113
114 versionMapper.AddSpecific(gv.WithKind(strings.ToLower(resource.Kind)), plural, singular, scope)
115 versionMapper.AddSpecific(gv.WithKind(resource.Kind), plural, singular, scope)
116
117 versionMapper.Add(gv.WithKind(resource.Kind+"List"), scope)
118 }
119
120 versionMapper.Add(gv.WithKind("List"), meta.RESTScopeRoot)
121 unionMapper = append(unionMapper, versionMapper)
122 }
123 }
124
125 for _, group := range groupPriority {
126 resourcePriority = append(resourcePriority, schema.GroupVersionResource{
127 Group: group,
128 Version: meta.AnyVersion,
129 Resource: meta.AnyResource,
130 })
131 kindPriority = append(kindPriority, schema.GroupVersionKind{
132 Group: group,
133 Version: meta.AnyVersion,
134 Kind: meta.AnyKind,
135 })
136 }
137
138 return meta.PriorityRESTMapper{
139 Delegate: unionMapper,
140 ResourcePriority: resourcePriority,
141 KindPriority: kindPriority,
142 }
143 }
144
145
146
147 func GetAPIGroupResources(cl discovery.DiscoveryInterface) ([]*APIGroupResources, error) {
148 gs, rs, err := cl.ServerGroupsAndResources()
149 if rs == nil || gs == nil {
150 return nil, err
151
152 }
153 rsm := map[string]*metav1.APIResourceList{}
154 for _, r := range rs {
155 rsm[r.GroupVersion] = r
156 }
157
158 var result []*APIGroupResources
159 for _, group := range gs {
160 groupResources := &APIGroupResources{
161 Group: *group,
162 VersionedResources: make(map[string][]metav1.APIResource),
163 }
164 for _, version := range group.Versions {
165 resources, ok := rsm[version.GroupVersion]
166 if !ok {
167 continue
168 }
169 groupResources.VersionedResources[version.Version] = resources.APIResources
170 }
171 result = append(result, groupResources)
172 }
173 return result, nil
174 }
175
176
177
178
179 type DeferredDiscoveryRESTMapper struct {
180 initMu sync.Mutex
181 delegate meta.RESTMapper
182 cl discovery.CachedDiscoveryInterface
183 }
184
185
186
187
188 func NewDeferredDiscoveryRESTMapper(cl discovery.CachedDiscoveryInterface) *DeferredDiscoveryRESTMapper {
189 return &DeferredDiscoveryRESTMapper{
190 cl: cl,
191 }
192 }
193
194 func (d *DeferredDiscoveryRESTMapper) getDelegate() (meta.RESTMapper, error) {
195 d.initMu.Lock()
196 defer d.initMu.Unlock()
197
198 if d.delegate != nil {
199 return d.delegate, nil
200 }
201
202 groupResources, err := GetAPIGroupResources(d.cl)
203 if err != nil {
204 return nil, err
205 }
206
207 d.delegate = NewDiscoveryRESTMapper(groupResources)
208 return d.delegate, nil
209 }
210
211
212
213 func (d *DeferredDiscoveryRESTMapper) Reset() {
214 klog.V(5).Info("Invalidating discovery information")
215
216 d.initMu.Lock()
217 defer d.initMu.Unlock()
218
219 d.cl.Invalidate()
220 d.delegate = nil
221 }
222
223
224
225 func (d *DeferredDiscoveryRESTMapper) KindFor(resource schema.GroupVersionResource) (gvk schema.GroupVersionKind, err error) {
226 del, err := d.getDelegate()
227 if err != nil {
228 return schema.GroupVersionKind{}, err
229 }
230 gvk, err = del.KindFor(resource)
231 if err != nil && !d.cl.Fresh() {
232 d.Reset()
233 gvk, err = d.KindFor(resource)
234 }
235 return
236 }
237
238
239
240 func (d *DeferredDiscoveryRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvks []schema.GroupVersionKind, err error) {
241 del, err := d.getDelegate()
242 if err != nil {
243 return nil, err
244 }
245 gvks, err = del.KindsFor(resource)
246 if len(gvks) == 0 && !d.cl.Fresh() {
247 d.Reset()
248 gvks, err = d.KindsFor(resource)
249 }
250 return
251 }
252
253
254
255 func (d *DeferredDiscoveryRESTMapper) ResourceFor(input schema.GroupVersionResource) (gvr schema.GroupVersionResource, err error) {
256 del, err := d.getDelegate()
257 if err != nil {
258 return schema.GroupVersionResource{}, err
259 }
260 gvr, err = del.ResourceFor(input)
261 if err != nil && !d.cl.Fresh() {
262 d.Reset()
263 gvr, err = d.ResourceFor(input)
264 }
265 return
266 }
267
268
269
270 func (d *DeferredDiscoveryRESTMapper) ResourcesFor(input schema.GroupVersionResource) (gvrs []schema.GroupVersionResource, err error) {
271 del, err := d.getDelegate()
272 if err != nil {
273 return nil, err
274 }
275 gvrs, err = del.ResourcesFor(input)
276 if len(gvrs) == 0 && !d.cl.Fresh() {
277 d.Reset()
278 gvrs, err = d.ResourcesFor(input)
279 }
280 return
281 }
282
283
284
285 func (d *DeferredDiscoveryRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (m *meta.RESTMapping, err error) {
286 del, err := d.getDelegate()
287 if err != nil {
288 return nil, err
289 }
290 m, err = del.RESTMapping(gk, versions...)
291 if err != nil && !d.cl.Fresh() {
292 d.Reset()
293 m, err = d.RESTMapping(gk, versions...)
294 }
295 return
296 }
297
298
299
300
301 func (d *DeferredDiscoveryRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) (ms []*meta.RESTMapping, err error) {
302 del, err := d.getDelegate()
303 if err != nil {
304 return nil, err
305 }
306 ms, err = del.RESTMappings(gk, versions...)
307 if len(ms) == 0 && !d.cl.Fresh() {
308 d.Reset()
309 ms, err = d.RESTMappings(gk, versions...)
310 }
311 return
312 }
313
314
315
316 func (d *DeferredDiscoveryRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
317 del, err := d.getDelegate()
318 if err != nil {
319 return resource, err
320 }
321 singular, err = del.ResourceSingularizer(resource)
322 if err != nil && !d.cl.Fresh() {
323 d.Reset()
324 singular, err = d.ResourceSingularizer(resource)
325 }
326 return
327 }
328
329 func (d *DeferredDiscoveryRESTMapper) String() string {
330 del, err := d.getDelegate()
331 if err != nil {
332 return fmt.Sprintf("DeferredDiscoveryRESTMapper{%v}", err)
333 }
334 return fmt.Sprintf("DeferredDiscoveryRESTMapper{\n\t%v\n}", del)
335 }
336
337
338 var _ meta.ResettableRESTMapper = &DeferredDiscoveryRESTMapper{}
339
View as plain text