2020-10-21 05:49:41 +00:00
|
|
|
/*
|
|
|
|
Copyright 2018 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 client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2021-06-25 05:02:01 +00:00
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
2020-10-21 05:49:41 +00:00
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
|
|
|
"k8s.io/client-go/rest"
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
|
|
|
|
)
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// clientCache creates and caches rest clients and metadata for Kubernetes types.
|
2020-10-21 05:49:41 +00:00
|
|
|
type clientCache struct {
|
|
|
|
// config is the rest.Config to talk to an apiserver
|
|
|
|
config *rest.Config
|
|
|
|
|
|
|
|
// scheme maps go structs to GroupVersionKinds
|
|
|
|
scheme *runtime.Scheme
|
|
|
|
|
|
|
|
// mapper maps GroupVersionKinds to Resources
|
|
|
|
mapper meta.RESTMapper
|
|
|
|
|
|
|
|
// codecs are used to create a REST client for a gvk
|
|
|
|
codecs serializer.CodecFactory
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// structuredResourceByType caches structured type metadata
|
|
|
|
structuredResourceByType map[schema.GroupVersionKind]*resourceMeta
|
|
|
|
// unstructuredResourceByType caches unstructured type metadata
|
|
|
|
unstructuredResourceByType map[schema.GroupVersionKind]*resourceMeta
|
|
|
|
mu sync.RWMutex
|
2020-10-21 05:49:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
|
|
|
|
// If the object is a list, the resource represents the item's type instead.
|
2021-06-25 05:02:01 +00:00
|
|
|
func (c *clientCache) newResource(gvk schema.GroupVersionKind, isList, isUnstructured bool) (*resourceMeta, error) {
|
2020-10-21 05:49:41 +00:00
|
|
|
if strings.HasSuffix(gvk.Kind, "List") && isList {
|
|
|
|
// if this was a list, treat it as a request for the item's resource
|
|
|
|
gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
client, err := apiutil.RESTClientForGVK(gvk, isUnstructured, c.config, c.codecs)
|
2020-10-21 05:49:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &resourceMeta{Interface: client, mapping: mapping, gvk: gvk}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// getResource returns the resource meta information for the given type of object.
|
|
|
|
// If the object is a list, the resource represents the item's type instead.
|
|
|
|
func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
|
|
|
|
gvk, err := apiutil.GVKForObject(obj, c.scheme)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
_, isUnstructured := obj.(*unstructured.Unstructured)
|
|
|
|
_, isUnstructuredList := obj.(*unstructured.UnstructuredList)
|
|
|
|
isUnstructured = isUnstructured || isUnstructuredList
|
|
|
|
|
2020-10-21 05:49:41 +00:00
|
|
|
// It's better to do creation work twice than to not let multiple
|
|
|
|
// people make requests at once
|
|
|
|
c.mu.RLock()
|
2021-06-25 05:02:01 +00:00
|
|
|
resourceByType := c.structuredResourceByType
|
|
|
|
if isUnstructured {
|
|
|
|
resourceByType = c.unstructuredResourceByType
|
|
|
|
}
|
|
|
|
r, known := resourceByType[gvk]
|
2020-10-21 05:49:41 +00:00
|
|
|
c.mu.RUnlock()
|
|
|
|
|
|
|
|
if known {
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize a new Client
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
2021-06-25 05:02:01 +00:00
|
|
|
r, err = c.newResource(gvk, meta.IsListType(obj), isUnstructured)
|
2020-10-21 05:49:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-25 05:02:01 +00:00
|
|
|
resourceByType[gvk] = r
|
2020-10-21 05:49:41 +00:00
|
|
|
return r, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// getObjMeta returns objMeta containing both type and object metadata and state.
|
2020-10-21 05:49:41 +00:00
|
|
|
func (c *clientCache) getObjMeta(obj runtime.Object) (*objMeta, error) {
|
|
|
|
r, err := c.getResource(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
m, err := meta.Accessor(obj)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &objMeta{resourceMeta: r, Object: m}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// resourceMeta caches state for a Kubernetes type.
|
|
|
|
type resourceMeta struct {
|
|
|
|
// client is the rest client used to talk to the apiserver
|
|
|
|
rest.Interface
|
|
|
|
// gvk is the GroupVersionKind of the resourceMeta
|
|
|
|
gvk schema.GroupVersionKind
|
|
|
|
// mapping is the rest mapping
|
|
|
|
mapping *meta.RESTMapping
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// isNamespaced returns true if the type is namespaced.
|
2020-10-21 05:49:41 +00:00
|
|
|
func (r *resourceMeta) isNamespaced() bool {
|
|
|
|
return r.mapping.Scope.Name() != meta.RESTScopeNameRoot
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// resource returns the resource name of the type.
|
2020-10-21 05:49:41 +00:00
|
|
|
func (r *resourceMeta) resource() string {
|
|
|
|
return r.mapping.Resource.Resource
|
|
|
|
}
|
|
|
|
|
2021-06-25 05:02:01 +00:00
|
|
|
// objMeta stores type and object information about a Kubernetes type.
|
2020-10-21 05:49:41 +00:00
|
|
|
type objMeta struct {
|
|
|
|
// resourceMeta contains type information for the object
|
|
|
|
*resourceMeta
|
|
|
|
|
|
|
|
// Object contains meta data for the object instance
|
|
|
|
metav1.Object
|
|
|
|
}
|