volcano.sh/volcano@v1.9.0/pkg/scheduler/capabilities/volumebinding/assume_cache.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 volumebinding
    18  
    19  import (
    20  	"fmt"
    21  	"strconv"
    22  	"sync"
    23  
    24  	"k8s.io/klog/v2"
    25  
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	"k8s.io/client-go/tools/cache"
    29  	storagehelpers "k8s.io/component-helpers/storage/volume"
    30  )
    31  
    32  // AssumeCache is a cache on top of the informer that allows for updating
    33  // objects outside of informer events and also restoring the informer
    34  // cache's version of the object.  Objects are assumed to be
    35  // Kubernetes API objects that implement meta.Interface
    36  type AssumeCache interface {
    37  	// Assume updates the object in-memory only
    38  	Assume(obj interface{}) error
    39  
    40  	// Restore the informer cache's version of the object
    41  	Restore(objName string)
    42  
    43  	// Get the object by name
    44  	Get(objName string) (interface{}, error)
    45  
    46  	// GetAPIObj gets the API object by name
    47  	GetAPIObj(objName string) (interface{}, error)
    48  
    49  	// List all the objects in the cache
    50  	List(indexObj interface{}) []interface{}
    51  }
    52  
    53  type errWrongType struct {
    54  	typeName string
    55  	object   interface{}
    56  }
    57  
    58  func (e *errWrongType) Error() string {
    59  	return fmt.Sprintf("could not convert object to type %v: %+v", e.typeName, e.object)
    60  }
    61  
    62  type errNotFound struct {
    63  	typeName   string
    64  	objectName string
    65  }
    66  
    67  func (e *errNotFound) Error() string {
    68  	return fmt.Sprintf("could not find %v %q", e.typeName, e.objectName)
    69  }
    70  
    71  type errObjectName struct {
    72  	detailedErr error
    73  }
    74  
    75  func (e *errObjectName) Error() string {
    76  	return fmt.Sprintf("failed to get object name: %v", e.detailedErr)
    77  }
    78  
    79  // assumeCache stores two pointers to represent a single object:
    80  //   - The pointer to the informer object.
    81  //   - The pointer to the latest object, which could be the same as
    82  //     the informer object, or an in-memory object.
    83  //
    84  // An informer update always overrides the latest object pointer.
    85  //
    86  // Assume() only updates the latest object pointer.
    87  // Restore() sets the latest object pointer back to the informer object.
    88  // Get/List() always returns the latest object pointer.
    89  type assumeCache struct {
    90  	// The logger that was chosen when setting up the cache.
    91  	// Will be used for all operations.
    92  	logger klog.Logger
    93  
    94  	// Synchronizes updates to store
    95  	rwMutex sync.RWMutex
    96  
    97  	// describes the object stored
    98  	description string
    99  
   100  	// Stores objInfo pointers
   101  	store cache.Indexer
   102  
   103  	// Index function for object
   104  	indexFunc cache.IndexFunc
   105  	indexName string
   106  }
   107  
   108  type objInfo struct {
   109  	// name of the object
   110  	name string
   111  
   112  	// Latest version of object could be cached-only or from informer
   113  	latestObj interface{}
   114  
   115  	// Latest object from informer
   116  	apiObj interface{}
   117  }
   118  
   119  func objInfoKeyFunc(obj interface{}) (string, error) {
   120  	objInfo, ok := obj.(*objInfo)
   121  	if !ok {
   122  		return "", &errWrongType{"objInfo", obj}
   123  	}
   124  	return objInfo.name, nil
   125  }
   126  
   127  func (c *assumeCache) objInfoIndexFunc(obj interface{}) ([]string, error) {
   128  	objInfo, ok := obj.(*objInfo)
   129  	if !ok {
   130  		return []string{""}, &errWrongType{"objInfo", obj}
   131  	}
   132  	return c.indexFunc(objInfo.latestObj)
   133  }
   134  
   135  // NewAssumeCache creates an assume cache for general objects.
   136  func NewAssumeCache(logger klog.Logger, informer cache.SharedIndexInformer, description, indexName string, indexFunc cache.IndexFunc) AssumeCache {
   137  	c := &assumeCache{
   138  		logger:      logger,
   139  		description: description,
   140  		indexFunc:   indexFunc,
   141  		indexName:   indexName,
   142  	}
   143  	indexers := cache.Indexers{}
   144  	if indexName != "" && indexFunc != nil {
   145  		indexers[indexName] = c.objInfoIndexFunc
   146  	}
   147  	c.store = cache.NewIndexer(objInfoKeyFunc, indexers)
   148  
   149  	// Unit tests don't use informers
   150  	if informer != nil {
   151  		informer.AddEventHandler(
   152  			cache.ResourceEventHandlerFuncs{
   153  				AddFunc:    c.add,
   154  				UpdateFunc: c.update,
   155  				DeleteFunc: c.delete,
   156  			},
   157  		)
   158  	}
   159  	return c
   160  }
   161  
   162  func (c *assumeCache) add(obj interface{}) {
   163  	if obj == nil {
   164  		return
   165  	}
   166  
   167  	name, err := cache.MetaNamespaceKeyFunc(obj)
   168  	if err != nil {
   169  		c.logger.Error(&errObjectName{err}, "Add failed")
   170  		return
   171  	}
   172  
   173  	c.rwMutex.Lock()
   174  	defer c.rwMutex.Unlock()
   175  
   176  	if objInfo, _ := c.getObjInfo(name); objInfo != nil {
   177  		newVersion, err := c.getObjVersion(name, obj)
   178  		if err != nil {
   179  			c.logger.Error(err, "Add failed: couldn't get object version")
   180  			return
   181  		}
   182  
   183  		storedVersion, err := c.getObjVersion(name, objInfo.latestObj)
   184  		if err != nil {
   185  			c.logger.Error(err, "Add failed: couldn't get stored object version")
   186  			return
   187  		}
   188  
   189  		// Only update object if version is newer.
   190  		// This is so we don't override assumed objects due to informer resync.
   191  		if newVersion <= storedVersion {
   192  			c.logger.V(10).Info("Skip adding object to assume cache because version is not newer than storedVersion", "description", c.description, "cacheKey", name, "newVersion", newVersion, "storedVersion", storedVersion)
   193  			return
   194  		}
   195  	}
   196  
   197  	objInfo := &objInfo{name: name, latestObj: obj, apiObj: obj}
   198  	if err = c.store.Update(objInfo); err != nil {
   199  		c.logger.Info("Error occurred while updating stored object", "err", err)
   200  	} else {
   201  		c.logger.V(10).Info("Adding object to assume cache", "description", c.description, "cacheKey", name, "assumeCache", obj)
   202  	}
   203  }
   204  
   205  func (c *assumeCache) update(oldObj interface{}, newObj interface{}) {
   206  	c.add(newObj)
   207  }
   208  
   209  func (c *assumeCache) delete(obj interface{}) {
   210  	if obj == nil {
   211  		return
   212  	}
   213  
   214  	name, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
   215  	if err != nil {
   216  		c.logger.Error(&errObjectName{err}, "Failed to delete")
   217  		return
   218  	}
   219  
   220  	c.rwMutex.Lock()
   221  	defer c.rwMutex.Unlock()
   222  
   223  	objInfo := &objInfo{name: name}
   224  	err = c.store.Delete(objInfo)
   225  	if err != nil {
   226  		c.logger.Error(err, "Failed to delete", "description", c.description, "cacheKey", name)
   227  	}
   228  }
   229  
   230  func (c *assumeCache) getObjVersion(name string, obj interface{}) (int64, error) {
   231  	objAccessor, err := meta.Accessor(obj)
   232  	if err != nil {
   233  		return -1, err
   234  	}
   235  
   236  	objResourceVersion, err := strconv.ParseInt(objAccessor.GetResourceVersion(), 10, 64)
   237  	if err != nil {
   238  		return -1, fmt.Errorf("error parsing ResourceVersion %q for %v %q: %s", objAccessor.GetResourceVersion(), c.description, name, err)
   239  	}
   240  	return objResourceVersion, nil
   241  }
   242  
   243  func (c *assumeCache) getObjInfo(name string) (*objInfo, error) {
   244  	obj, ok, err := c.store.GetByKey(name)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	if !ok {
   249  		return nil, &errNotFound{c.description, name}
   250  	}
   251  
   252  	objInfo, ok := obj.(*objInfo)
   253  	if !ok {
   254  		return nil, &errWrongType{"objInfo", obj}
   255  	}
   256  	return objInfo, nil
   257  }
   258  
   259  func (c *assumeCache) Get(objName string) (interface{}, error) {
   260  	c.rwMutex.RLock()
   261  	defer c.rwMutex.RUnlock()
   262  
   263  	objInfo, err := c.getObjInfo(objName)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  	return objInfo.latestObj, nil
   268  }
   269  
   270  func (c *assumeCache) GetAPIObj(objName string) (interface{}, error) {
   271  	c.rwMutex.RLock()
   272  	defer c.rwMutex.RUnlock()
   273  
   274  	objInfo, err := c.getObjInfo(objName)
   275  	if err != nil {
   276  		return nil, err
   277  	}
   278  	return objInfo.apiObj, nil
   279  }
   280  
   281  func (c *assumeCache) List(indexObj interface{}) []interface{} {
   282  	c.rwMutex.RLock()
   283  	defer c.rwMutex.RUnlock()
   284  
   285  	allObjs := []interface{}{}
   286  	objs, err := c.store.Index(c.indexName, &objInfo{latestObj: indexObj})
   287  	if err != nil {
   288  		c.logger.Error(err, "List index error")
   289  		return nil
   290  	}
   291  
   292  	for _, obj := range objs {
   293  		objInfo, ok := obj.(*objInfo)
   294  		if !ok {
   295  			c.logger.Error(&errWrongType{"objInfo", obj}, "List error")
   296  			continue
   297  		}
   298  		allObjs = append(allObjs, objInfo.latestObj)
   299  	}
   300  	return allObjs
   301  }
   302  
   303  func (c *assumeCache) Assume(obj interface{}) error {
   304  	name, err := cache.MetaNamespaceKeyFunc(obj)
   305  	if err != nil {
   306  		return &errObjectName{err}
   307  	}
   308  
   309  	c.rwMutex.Lock()
   310  	defer c.rwMutex.Unlock()
   311  
   312  	objInfo, err := c.getObjInfo(name)
   313  	if err != nil {
   314  		return err
   315  	}
   316  
   317  	newVersion, err := c.getObjVersion(name, obj)
   318  	if err != nil {
   319  		return err
   320  	}
   321  
   322  	storedVersion, err := c.getObjVersion(name, objInfo.latestObj)
   323  	if err != nil {
   324  		return err
   325  	}
   326  
   327  	if newVersion < storedVersion {
   328  		return fmt.Errorf("%v %q is out of sync (stored: %d, assume: %d)", c.description, name, storedVersion, newVersion)
   329  	}
   330  
   331  	// Only update the cached object
   332  	objInfo.latestObj = obj
   333  	c.logger.V(4).Info("Assumed object", "description", c.description, "cacheKey", name, "version", newVersion)
   334  	return nil
   335  }
   336  
   337  func (c *assumeCache) Restore(objName string) {
   338  	c.rwMutex.Lock()
   339  	defer c.rwMutex.Unlock()
   340  
   341  	objInfo, err := c.getObjInfo(objName)
   342  	if err != nil {
   343  		// This could be expected if object got deleted
   344  		c.logger.V(5).Info("Restore object", "description", c.description, "cacheKey", objName, "err", err)
   345  	} else {
   346  		objInfo.latestObj = objInfo.apiObj
   347  		c.logger.V(4).Info("Restored object", "description", c.description, "cacheKey", objName)
   348  	}
   349  }
   350  
   351  // PVAssumeCache is a AssumeCache for PersistentVolume objects
   352  type PVAssumeCache interface {
   353  	AssumeCache
   354  
   355  	GetPV(pvName string) (*v1.PersistentVolume, error)
   356  	GetAPIPV(pvName string) (*v1.PersistentVolume, error)
   357  	ListPVs(storageClassName string) []*v1.PersistentVolume
   358  }
   359  
   360  type pvAssumeCache struct {
   361  	AssumeCache
   362  	logger klog.Logger
   363  }
   364  
   365  func pvStorageClassIndexFunc(obj interface{}) ([]string, error) {
   366  	if pv, ok := obj.(*v1.PersistentVolume); ok {
   367  		return []string{storagehelpers.GetPersistentVolumeClass(pv)}, nil
   368  	}
   369  	return []string{""}, fmt.Errorf("object is not a v1.PersistentVolume: %v", obj)
   370  }
   371  
   372  // NewPVAssumeCache creates a PV assume cache.
   373  func NewPVAssumeCache(logger klog.Logger, informer cache.SharedIndexInformer) PVAssumeCache {
   374  	logger = klog.LoggerWithName(logger, "PV Cache")
   375  	return &pvAssumeCache{
   376  		AssumeCache: NewAssumeCache(logger, informer, "v1.PersistentVolume", "storageclass", pvStorageClassIndexFunc),
   377  		logger:      logger,
   378  	}
   379  }
   380  
   381  func (c *pvAssumeCache) GetPV(pvName string) (*v1.PersistentVolume, error) {
   382  	obj, err := c.Get(pvName)
   383  	if err != nil {
   384  		return nil, err
   385  	}
   386  
   387  	pv, ok := obj.(*v1.PersistentVolume)
   388  	if !ok {
   389  		return nil, &errWrongType{"v1.PersistentVolume", obj}
   390  	}
   391  	return pv, nil
   392  }
   393  
   394  func (c *pvAssumeCache) GetAPIPV(pvName string) (*v1.PersistentVolume, error) {
   395  	obj, err := c.GetAPIObj(pvName)
   396  	if err != nil {
   397  		return nil, err
   398  	}
   399  	pv, ok := obj.(*v1.PersistentVolume)
   400  	if !ok {
   401  		return nil, &errWrongType{"v1.PersistentVolume", obj}
   402  	}
   403  	return pv, nil
   404  }
   405  
   406  func (c *pvAssumeCache) ListPVs(storageClassName string) []*v1.PersistentVolume {
   407  	objs := c.List(&v1.PersistentVolume{
   408  		Spec: v1.PersistentVolumeSpec{
   409  			StorageClassName: storageClassName,
   410  		},
   411  	})
   412  	pvs := []*v1.PersistentVolume{}
   413  	for _, obj := range objs {
   414  		pv, ok := obj.(*v1.PersistentVolume)
   415  		if !ok {
   416  			c.logger.Error(&errWrongType{"v1.PersistentVolume", obj}, "ListPVs")
   417  			continue
   418  		}
   419  		pvs = append(pvs, pv)
   420  	}
   421  	return pvs
   422  }
   423  
   424  // PVCAssumeCache is a AssumeCache for PersistentVolumeClaim objects
   425  type PVCAssumeCache interface {
   426  	AssumeCache
   427  
   428  	// GetPVC returns the PVC from the cache with given pvcKey.
   429  	// pvcKey is the result of MetaNamespaceKeyFunc on PVC obj
   430  	GetPVC(pvcKey string) (*v1.PersistentVolumeClaim, error)
   431  	GetAPIPVC(pvcKey string) (*v1.PersistentVolumeClaim, error)
   432  }
   433  
   434  type pvcAssumeCache struct {
   435  	AssumeCache
   436  	logger klog.Logger
   437  }
   438  
   439  // NewPVCAssumeCache creates a PVC assume cache.
   440  func NewPVCAssumeCache(logger klog.Logger, informer cache.SharedIndexInformer) PVCAssumeCache {
   441  	logger = klog.LoggerWithName(logger, "PVC Cache")
   442  	return &pvcAssumeCache{
   443  		AssumeCache: NewAssumeCache(logger, informer, "v1.PersistentVolumeClaim", "", nil),
   444  		logger:      logger,
   445  	}
   446  }
   447  
   448  func (c *pvcAssumeCache) GetPVC(pvcKey string) (*v1.PersistentVolumeClaim, error) {
   449  	obj, err := c.Get(pvcKey)
   450  	if err != nil {
   451  		return nil, err
   452  	}
   453  
   454  	pvc, ok := obj.(*v1.PersistentVolumeClaim)
   455  	if !ok {
   456  		return nil, &errWrongType{"v1.PersistentVolumeClaim", obj}
   457  	}
   458  	return pvc, nil
   459  }
   460  
   461  func (c *pvcAssumeCache) GetAPIPVC(pvcKey string) (*v1.PersistentVolumeClaim, error) {
   462  	obj, err := c.GetAPIObj(pvcKey)
   463  	if err != nil {
   464  		return nil, err
   465  	}
   466  	pvc, ok := obj.(*v1.PersistentVolumeClaim)
   467  	if !ok {
   468  		return nil, &errWrongType{"v1.PersistentVolumeClaim", obj}
   469  	}
   470  	return pvc, nil
   471  }