1
16
17 package restmapper
18
19 import (
20 "fmt"
21 "strings"
22
23 "k8s.io/klog/v2"
24
25 "k8s.io/apimachinery/pkg/api/meta"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/runtime/schema"
28 "k8s.io/client-go/discovery"
29 )
30
31
32 type shortcutExpander struct {
33 RESTMapper meta.RESTMapper
34
35 discoveryClient discovery.DiscoveryInterface
36
37 warningHandler func(string)
38 }
39
40 var _ meta.ResettableRESTMapper = shortcutExpander{}
41
42
43 func NewShortcutExpander(delegate meta.RESTMapper, client discovery.DiscoveryInterface, warningHandler func(string)) meta.RESTMapper {
44 return shortcutExpander{RESTMapper: delegate, discoveryClient: client, warningHandler: warningHandler}
45 }
46
47
48 func (e shortcutExpander) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
49
50
51
52
53
54
55
56 gvk, err := e.RESTMapper.KindFor(e.expandResourceShortcut(resource))
57 if meta.IsNoMatchError(err) {
58 return e.RESTMapper.KindFor(e.expandResourceShortcut(resource))
59 }
60 return gvk, err
61 }
62
63
64 func (e shortcutExpander) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
65 return e.RESTMapper.KindsFor(e.expandResourceShortcut(resource))
66 }
67
68
69 func (e shortcutExpander) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
70 return e.RESTMapper.ResourcesFor(e.expandResourceShortcut(resource))
71 }
72
73
74 func (e shortcutExpander) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
75 return e.RESTMapper.ResourceFor(e.expandResourceShortcut(resource))
76 }
77
78
79 func (e shortcutExpander) ResourceSingularizer(resource string) (string, error) {
80 return e.RESTMapper.ResourceSingularizer(e.expandResourceShortcut(schema.GroupVersionResource{Resource: resource}).Resource)
81 }
82
83
84 func (e shortcutExpander) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
85 return e.RESTMapper.RESTMapping(gk, versions...)
86 }
87
88
89 func (e shortcutExpander) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
90 return e.RESTMapper.RESTMappings(gk, versions...)
91 }
92
93
94
95
96
97 func (e shortcutExpander) getShortcutMappings() ([]*metav1.APIResourceList, []resourceShortcuts, error) {
98 res := []resourceShortcuts{}
99
100
101 _, apiResList, err := e.discoveryClient.ServerGroupsAndResources()
102 if err != nil {
103 klog.V(1).Infof("Error loading discovery information: %v", err)
104 }
105 for _, apiResources := range apiResList {
106 gv, err := schema.ParseGroupVersion(apiResources.GroupVersion)
107 if err != nil {
108 klog.V(1).Infof("Unable to parse groupversion = %s due to = %s", apiResources.GroupVersion, err.Error())
109 continue
110 }
111 for _, apiRes := range apiResources.APIResources {
112 for _, shortName := range apiRes.ShortNames {
113 rs := resourceShortcuts{
114 ShortForm: schema.GroupResource{Group: gv.Group, Resource: shortName},
115 LongForm: schema.GroupResource{Group: gv.Group, Resource: apiRes.Name},
116 }
117 res = append(res, rs)
118 }
119 }
120 }
121
122 return apiResList, res, nil
123 }
124
125
126
127
128
129 func (e shortcutExpander) expandResourceShortcut(resource schema.GroupVersionResource) schema.GroupVersionResource {
130
131 if allResources, shortcutResources, err := e.getShortcutMappings(); err == nil {
132
133 for _, apiResources := range allResources {
134 gv, err := schema.ParseGroupVersion(apiResources.GroupVersion)
135 if err != nil {
136 continue
137 }
138 if len(resource.Group) != 0 && resource.Group != gv.Group {
139 continue
140 }
141 for _, apiRes := range apiResources.APIResources {
142 if resource.Resource == apiRes.Name {
143 return resource
144 }
145 if resource.Resource == apiRes.SingularName {
146 return resource
147 }
148 }
149 }
150
151 found := false
152 var rsc schema.GroupVersionResource
153 warnedAmbiguousShortcut := make(map[schema.GroupResource]bool)
154 for _, item := range shortcutResources {
155 if len(resource.Group) != 0 && resource.Group != item.ShortForm.Group {
156 continue
157 }
158 if resource.Resource == item.ShortForm.Resource {
159 if found {
160 if item.LongForm.Group == rsc.Group && item.LongForm.Resource == rsc.Resource {
161
162
163
164 continue
165 }
166 if !warnedAmbiguousShortcut[item.LongForm] {
167 if e.warningHandler != nil {
168 e.warningHandler(fmt.Sprintf("short name %q could also match lower priority resource %s", resource.Resource, item.LongForm.String()))
169 }
170 warnedAmbiguousShortcut[item.LongForm] = true
171 }
172 continue
173 }
174 rsc.Resource = item.LongForm.Resource
175 rsc.Group = item.LongForm.Group
176 found = true
177 }
178 }
179 if found {
180 return rsc
181 }
182
183
184 if len(resource.Group) == 0 {
185 return resource
186 }
187 for _, item := range shortcutResources {
188 if !strings.HasPrefix(item.ShortForm.Group, resource.Group) {
189 continue
190 }
191 if resource.Resource == item.ShortForm.Resource {
192 resource.Resource = item.LongForm.Resource
193 resource.Group = item.LongForm.Group
194 return resource
195 }
196 }
197 }
198
199 return resource
200 }
201
202 func (e shortcutExpander) Reset() {
203 meta.MaybeResetRESTMapper(e.RESTMapper)
204 }
205
206
207
208 type resourceShortcuts struct {
209 ShortForm schema.GroupResource
210 LongForm schema.GroupResource
211 }
212
View as plain text