...

Source file src/sigs.k8s.io/controller-runtime/pkg/client/client_rest_resources.go

Documentation: sigs.k8s.io/controller-runtime/pkg/client

     1  /*
     2  Copyright 2018 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 client
    18  
    19  import (
    20  	"net/http"
    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"
    27  	"k8s.io/apimachinery/pkg/runtime/schema"
    28  	"k8s.io/apimachinery/pkg/runtime/serializer"
    29  	"k8s.io/client-go/rest"
    30  	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
    31  )
    32  
    33  // clientRestResources creates and stores rest clients and metadata for Kubernetes types.
    34  type clientRestResources struct {
    35  	// httpClient is the http client to use for requests
    36  	httpClient *http.Client
    37  
    38  	// config is the rest.Config to talk to an apiserver
    39  	config *rest.Config
    40  
    41  	// scheme maps go structs to GroupVersionKinds
    42  	scheme *runtime.Scheme
    43  
    44  	// mapper maps GroupVersionKinds to Resources
    45  	mapper meta.RESTMapper
    46  
    47  	// codecs are used to create a REST client for a gvk
    48  	codecs serializer.CodecFactory
    49  
    50  	// structuredResourceByType stores structured type metadata
    51  	structuredResourceByType map[schema.GroupVersionKind]*resourceMeta
    52  	// unstructuredResourceByType stores unstructured type metadata
    53  	unstructuredResourceByType map[schema.GroupVersionKind]*resourceMeta
    54  	mu                         sync.RWMutex
    55  }
    56  
    57  // newResource maps obj to a Kubernetes Resource and constructs a client for that Resource.
    58  // If the object is a list, the resource represents the item's type instead.
    59  func (c *clientRestResources) newResource(gvk schema.GroupVersionKind, isList, isUnstructured bool) (*resourceMeta, error) {
    60  	if strings.HasSuffix(gvk.Kind, "List") && isList {
    61  		// if this was a list, treat it as a request for the item's resource
    62  		gvk.Kind = gvk.Kind[:len(gvk.Kind)-4]
    63  	}
    64  
    65  	client, err := apiutil.RESTClientForGVK(gvk, isUnstructured, c.config, c.codecs, c.httpClient)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	mapping, err := c.mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	return &resourceMeta{Interface: client, mapping: mapping, gvk: gvk}, nil
    74  }
    75  
    76  // getResource returns the resource meta information for the given type of object.
    77  // If the object is a list, the resource represents the item's type instead.
    78  func (c *clientRestResources) getResource(obj runtime.Object) (*resourceMeta, error) {
    79  	gvk, err := apiutil.GVKForObject(obj, c.scheme)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	_, isUnstructured := obj.(runtime.Unstructured)
    85  
    86  	// It's better to do creation work twice than to not let multiple
    87  	// people make requests at once
    88  	c.mu.RLock()
    89  	resourceByType := c.structuredResourceByType
    90  	if isUnstructured {
    91  		resourceByType = c.unstructuredResourceByType
    92  	}
    93  	r, known := resourceByType[gvk]
    94  	c.mu.RUnlock()
    95  
    96  	if known {
    97  		return r, nil
    98  	}
    99  
   100  	// Initialize a new Client
   101  	c.mu.Lock()
   102  	defer c.mu.Unlock()
   103  	r, err = c.newResource(gvk, meta.IsListType(obj), isUnstructured)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	resourceByType[gvk] = r
   108  	return r, err
   109  }
   110  
   111  // getObjMeta returns objMeta containing both type and object metadata and state.
   112  func (c *clientRestResources) getObjMeta(obj runtime.Object) (*objMeta, error) {
   113  	r, err := c.getResource(obj)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	m, err := meta.Accessor(obj)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return &objMeta{resourceMeta: r, Object: m}, err
   122  }
   123  
   124  // resourceMeta stores state for a Kubernetes type.
   125  type resourceMeta struct {
   126  	// client is the rest client used to talk to the apiserver
   127  	rest.Interface
   128  	// gvk is the GroupVersionKind of the resourceMeta
   129  	gvk schema.GroupVersionKind
   130  	// mapping is the rest mapping
   131  	mapping *meta.RESTMapping
   132  }
   133  
   134  // isNamespaced returns true if the type is namespaced.
   135  func (r *resourceMeta) isNamespaced() bool {
   136  	return r.mapping.Scope.Name() != meta.RESTScopeNameRoot
   137  }
   138  
   139  // resource returns the resource name of the type.
   140  func (r *resourceMeta) resource() string {
   141  	return r.mapping.Resource.Resource
   142  }
   143  
   144  // objMeta stores type and object information about a Kubernetes type.
   145  type objMeta struct {
   146  	// resourceMeta contains type information for the object
   147  	*resourceMeta
   148  
   149  	// Object contains meta data for the object instance
   150  	metav1.Object
   151  }
   152  

View as plain text