mirror of https://github.com/k3s-io/k3s
198 lines
6.9 KiB
Go
198 lines
6.9 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package scale
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
serializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
|
"k8s.io/client-go/discovery"
|
|
scalescheme "k8s.io/client-go/scale/scheme"
|
|
scaleappsint "k8s.io/client-go/scale/scheme/appsint"
|
|
scaleappsv1beta1 "k8s.io/client-go/scale/scheme/appsv1beta1"
|
|
scaleappsv1beta2 "k8s.io/client-go/scale/scheme/appsv1beta2"
|
|
scaleautoscaling "k8s.io/client-go/scale/scheme/autoscalingv1"
|
|
scaleextint "k8s.io/client-go/scale/scheme/extensionsint"
|
|
scaleext "k8s.io/client-go/scale/scheme/extensionsv1beta1"
|
|
)
|
|
|
|
// PreferredResourceMapper determines the preferred version of a resource to scale
|
|
type PreferredResourceMapper interface {
|
|
// ResourceFor takes a partial resource and returns the preferred resource.
|
|
ResourceFor(resource schema.GroupVersionResource) (preferredResource schema.GroupVersionResource, err error)
|
|
}
|
|
|
|
// Ensure a RESTMapper satisfies the PreferredResourceMapper interface
|
|
var _ PreferredResourceMapper = meta.RESTMapper(nil)
|
|
|
|
// ScaleKindResolver knows about the relationship between
|
|
// resources and the GroupVersionKind of their scale subresources.
|
|
type ScaleKindResolver interface {
|
|
// ScaleForResource returns the GroupVersionKind of the
|
|
// scale subresource for the given GroupVersionResource.
|
|
ScaleForResource(resource schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error)
|
|
}
|
|
|
|
// discoveryScaleResolver is a ScaleKindResolver that uses
|
|
// a DiscoveryInterface to associate resources with their
|
|
// scale-kinds
|
|
type discoveryScaleResolver struct {
|
|
discoveryClient discovery.ServerResourcesInterface
|
|
}
|
|
|
|
func (r *discoveryScaleResolver) ScaleForResource(inputRes schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) {
|
|
groupVerResources, err := r.discoveryClient.ServerResourcesForGroupVersion(inputRes.GroupVersion().String())
|
|
if err != nil {
|
|
return schema.GroupVersionKind{}, fmt.Errorf("unable to fetch discovery information for %s: %v", inputRes.String(), err)
|
|
}
|
|
|
|
for _, resource := range groupVerResources.APIResources {
|
|
resourceParts := strings.SplitN(resource.Name, "/", 2)
|
|
if len(resourceParts) != 2 || resourceParts[0] != inputRes.Resource || resourceParts[1] != "scale" {
|
|
// skip non-scale resources, or scales for resources that we're not looking for
|
|
continue
|
|
}
|
|
|
|
scaleGV := inputRes.GroupVersion()
|
|
if resource.Group != "" && resource.Version != "" {
|
|
scaleGV = schema.GroupVersion{
|
|
Group: resource.Group,
|
|
Version: resource.Version,
|
|
}
|
|
}
|
|
|
|
return scaleGV.WithKind(resource.Kind), nil
|
|
}
|
|
|
|
return schema.GroupVersionKind{}, fmt.Errorf("could not find scale subresource for %s in discovery information", inputRes.String())
|
|
}
|
|
|
|
// cachedScaleKindResolver is a ScaleKindResolver that caches results
|
|
// from another ScaleKindResolver, re-fetching on cache misses.
|
|
type cachedScaleKindResolver struct {
|
|
base ScaleKindResolver
|
|
|
|
cache map[schema.GroupVersionResource]schema.GroupVersionKind
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func (r *cachedScaleKindResolver) ScaleForResource(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
|
|
r.mu.RLock()
|
|
gvk, isCached := r.cache[resource]
|
|
r.mu.RUnlock()
|
|
if isCached {
|
|
return gvk, nil
|
|
}
|
|
|
|
// we could have multiple fetches of the same resources, but that's probably
|
|
// better than limiting to only one reader at once (mu.Mutex),
|
|
// or blocking checks for other resources while we fetch
|
|
// (mu.Lock before fetch).
|
|
gvk, err := r.base.ScaleForResource(resource)
|
|
if err != nil {
|
|
return schema.GroupVersionKind{}, err
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
r.cache[resource] = gvk
|
|
|
|
return gvk, nil
|
|
}
|
|
|
|
// NewDiscoveryScaleKindResolver creates a new ScaleKindResolver which uses information from the given
|
|
// disovery client to resolve the correct Scale GroupVersionKind for different resources.
|
|
func NewDiscoveryScaleKindResolver(client discovery.ServerResourcesInterface) ScaleKindResolver {
|
|
base := &discoveryScaleResolver{
|
|
discoveryClient: client,
|
|
}
|
|
|
|
return &cachedScaleKindResolver{
|
|
base: base,
|
|
cache: make(map[schema.GroupVersionResource]schema.GroupVersionKind),
|
|
}
|
|
}
|
|
|
|
// ScaleConverter knows how to convert between external scale versions.
|
|
type ScaleConverter struct {
|
|
scheme *runtime.Scheme
|
|
codecs serializer.CodecFactory
|
|
internalVersioner runtime.GroupVersioner
|
|
}
|
|
|
|
// NewScaleConverter creates a new ScaleConverter for converting between
|
|
// Scales in autoscaling/v1 and extensions/v1beta1.
|
|
func NewScaleConverter() *ScaleConverter {
|
|
scheme := runtime.NewScheme()
|
|
utilruntime.Must(scaleautoscaling.AddToScheme(scheme))
|
|
utilruntime.Must(scalescheme.AddToScheme(scheme))
|
|
utilruntime.Must(scaleext.AddToScheme(scheme))
|
|
utilruntime.Must(scaleextint.AddToScheme(scheme))
|
|
utilruntime.Must(scaleappsint.AddToScheme(scheme))
|
|
utilruntime.Must(scaleappsv1beta1.AddToScheme(scheme))
|
|
utilruntime.Must(scaleappsv1beta2.AddToScheme(scheme))
|
|
|
|
return &ScaleConverter{
|
|
scheme: scheme,
|
|
codecs: serializer.NewCodecFactory(scheme),
|
|
internalVersioner: runtime.NewMultiGroupVersioner(
|
|
scalescheme.SchemeGroupVersion,
|
|
schema.GroupKind{Group: scaleext.GroupName, Kind: "Scale"},
|
|
schema.GroupKind{Group: scaleautoscaling.GroupName, Kind: "Scale"},
|
|
schema.GroupKind{Group: scaleappsv1beta1.GroupName, Kind: "Scale"},
|
|
schema.GroupKind{Group: scaleappsv1beta2.GroupName, Kind: "Scale"},
|
|
),
|
|
}
|
|
}
|
|
|
|
// Scheme returns the scheme used by this scale converter.
|
|
func (c *ScaleConverter) Scheme() *runtime.Scheme {
|
|
return c.scheme
|
|
}
|
|
|
|
func (c *ScaleConverter) Codecs() serializer.CodecFactory {
|
|
return c.codecs
|
|
}
|
|
|
|
func (c *ScaleConverter) ScaleVersions() []schema.GroupVersion {
|
|
return []schema.GroupVersion{
|
|
scaleautoscaling.SchemeGroupVersion,
|
|
scalescheme.SchemeGroupVersion,
|
|
scaleext.SchemeGroupVersion,
|
|
scaleextint.SchemeGroupVersion,
|
|
scaleappsint.SchemeGroupVersion,
|
|
scaleappsv1beta1.SchemeGroupVersion,
|
|
scaleappsv1beta2.SchemeGroupVersion,
|
|
}
|
|
}
|
|
|
|
// ConvertToVersion converts the given *external* input object to the given output *external* output group-version.
|
|
func (c *ScaleConverter) ConvertToVersion(in runtime.Object, outVersion schema.GroupVersion) (runtime.Object, error) {
|
|
scaleInt, err := c.scheme.ConvertToVersion(in, c.internalVersioner)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c.scheme.ConvertToVersion(scaleInt, outVersion)
|
|
}
|