k8s.io/client-go@v0.31.1/scale/util.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package scale 18 19 import ( 20 "fmt" 21 "strings" 22 "sync" 23 24 "k8s.io/apimachinery/pkg/api/meta" 25 "k8s.io/apimachinery/pkg/runtime" 26 "k8s.io/apimachinery/pkg/runtime/schema" 27 serializer "k8s.io/apimachinery/pkg/runtime/serializer" 28 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 29 "k8s.io/client-go/discovery" 30 scalescheme "k8s.io/client-go/scale/scheme" 31 scaleappsint "k8s.io/client-go/scale/scheme/appsint" 32 scaleappsv1beta1 "k8s.io/client-go/scale/scheme/appsv1beta1" 33 scaleappsv1beta2 "k8s.io/client-go/scale/scheme/appsv1beta2" 34 scaleautoscaling "k8s.io/client-go/scale/scheme/autoscalingv1" 35 scaleextint "k8s.io/client-go/scale/scheme/extensionsint" 36 scaleext "k8s.io/client-go/scale/scheme/extensionsv1beta1" 37 ) 38 39 // PreferredResourceMapper determines the preferred version of a resource to scale 40 type PreferredResourceMapper interface { 41 // ResourceFor takes a partial resource and returns the preferred resource. 42 ResourceFor(resource schema.GroupVersionResource) (preferredResource schema.GroupVersionResource, err error) 43 } 44 45 // Ensure a RESTMapper satisfies the PreferredResourceMapper interface 46 var _ PreferredResourceMapper = meta.RESTMapper(nil) 47 48 // ScaleKindResolver knows about the relationship between 49 // resources and the GroupVersionKind of their scale subresources. 50 type ScaleKindResolver interface { 51 // ScaleForResource returns the GroupVersionKind of the 52 // scale subresource for the given GroupVersionResource. 53 ScaleForResource(resource schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) 54 } 55 56 // discoveryScaleResolver is a ScaleKindResolver that uses 57 // a DiscoveryInterface to associate resources with their 58 // scale-kinds 59 type discoveryScaleResolver struct { 60 discoveryClient discovery.ServerResourcesInterface 61 } 62 63 func (r *discoveryScaleResolver) ScaleForResource(inputRes schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) { 64 groupVerResources, err := r.discoveryClient.ServerResourcesForGroupVersion(inputRes.GroupVersion().String()) 65 if err != nil { 66 return schema.GroupVersionKind{}, fmt.Errorf("unable to fetch discovery information for %s: %v", inputRes.String(), err) 67 } 68 69 for _, resource := range groupVerResources.APIResources { 70 resourceParts := strings.SplitN(resource.Name, "/", 2) 71 if len(resourceParts) != 2 || resourceParts[0] != inputRes.Resource || resourceParts[1] != "scale" { 72 // skip non-scale resources, or scales for resources that we're not looking for 73 continue 74 } 75 76 scaleGV := inputRes.GroupVersion() 77 if resource.Group != "" && resource.Version != "" { 78 scaleGV = schema.GroupVersion{ 79 Group: resource.Group, 80 Version: resource.Version, 81 } 82 } 83 84 return scaleGV.WithKind(resource.Kind), nil 85 } 86 87 return schema.GroupVersionKind{}, fmt.Errorf("could not find scale subresource for %s in discovery information", inputRes.String()) 88 } 89 90 // cachedScaleKindResolver is a ScaleKindResolver that caches results 91 // from another ScaleKindResolver, re-fetching on cache misses. 92 type cachedScaleKindResolver struct { 93 base ScaleKindResolver 94 95 cache map[schema.GroupVersionResource]schema.GroupVersionKind 96 mu sync.RWMutex 97 } 98 99 func (r *cachedScaleKindResolver) ScaleForResource(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) { 100 r.mu.RLock() 101 gvk, isCached := r.cache[resource] 102 r.mu.RUnlock() 103 if isCached { 104 return gvk, nil 105 } 106 107 // we could have multiple fetches of the same resources, but that's probably 108 // better than limiting to only one reader at once (mu.Mutex), 109 // or blocking checks for other resources while we fetch 110 // (mu.Lock before fetch). 111 gvk, err := r.base.ScaleForResource(resource) 112 if err != nil { 113 return schema.GroupVersionKind{}, err 114 } 115 116 r.mu.Lock() 117 defer r.mu.Unlock() 118 r.cache[resource] = gvk 119 120 return gvk, nil 121 } 122 123 // NewDiscoveryScaleKindResolver creates a new ScaleKindResolver which uses information from the given 124 // disovery client to resolve the correct Scale GroupVersionKind for different resources. 125 func NewDiscoveryScaleKindResolver(client discovery.ServerResourcesInterface) ScaleKindResolver { 126 base := &discoveryScaleResolver{ 127 discoveryClient: client, 128 } 129 130 return &cachedScaleKindResolver{ 131 base: base, 132 cache: make(map[schema.GroupVersionResource]schema.GroupVersionKind), 133 } 134 } 135 136 // ScaleConverter knows how to convert between external scale versions. 137 type ScaleConverter struct { 138 scheme *runtime.Scheme 139 codecs serializer.CodecFactory 140 internalVersioner runtime.GroupVersioner 141 } 142 143 // NewScaleConverter creates a new ScaleConverter for converting between 144 // Scales in autoscaling/v1 and extensions/v1beta1. 145 func NewScaleConverter() *ScaleConverter { 146 scheme := runtime.NewScheme() 147 utilruntime.Must(scaleautoscaling.AddToScheme(scheme)) 148 utilruntime.Must(scalescheme.AddToScheme(scheme)) 149 utilruntime.Must(scaleext.AddToScheme(scheme)) 150 utilruntime.Must(scaleextint.AddToScheme(scheme)) 151 utilruntime.Must(scaleappsint.AddToScheme(scheme)) 152 utilruntime.Must(scaleappsv1beta1.AddToScheme(scheme)) 153 utilruntime.Must(scaleappsv1beta2.AddToScheme(scheme)) 154 155 return &ScaleConverter{ 156 scheme: scheme, 157 codecs: serializer.NewCodecFactory(scheme), 158 internalVersioner: runtime.NewMultiGroupVersioner( 159 scalescheme.SchemeGroupVersion, 160 schema.GroupKind{Group: scaleext.GroupName, Kind: "Scale"}, 161 schema.GroupKind{Group: scaleautoscaling.GroupName, Kind: "Scale"}, 162 schema.GroupKind{Group: scaleappsv1beta1.GroupName, Kind: "Scale"}, 163 schema.GroupKind{Group: scaleappsv1beta2.GroupName, Kind: "Scale"}, 164 ), 165 } 166 } 167 168 // Scheme returns the scheme used by this scale converter. 169 func (c *ScaleConverter) Scheme() *runtime.Scheme { 170 return c.scheme 171 } 172 173 func (c *ScaleConverter) Codecs() serializer.CodecFactory { 174 return c.codecs 175 } 176 177 func (c *ScaleConverter) ScaleVersions() []schema.GroupVersion { 178 return []schema.GroupVersion{ 179 scaleautoscaling.SchemeGroupVersion, 180 scalescheme.SchemeGroupVersion, 181 scaleext.SchemeGroupVersion, 182 scaleextint.SchemeGroupVersion, 183 scaleappsint.SchemeGroupVersion, 184 scaleappsv1beta1.SchemeGroupVersion, 185 scaleappsv1beta2.SchemeGroupVersion, 186 } 187 } 188 189 // ConvertToVersion converts the given *external* input object to the given output *external* output group-version. 190 func (c *ScaleConverter) ConvertToVersion(in runtime.Object, outVersion schema.GroupVersion) (runtime.Object, error) { 191 scaleInt, err := c.scheme.ConvertToVersion(in, c.internalVersioner) 192 if err != nil { 193 return nil, err 194 } 195 196 return c.scheme.ConvertToVersion(scaleInt, outVersion) 197 }