| /* |
| Copyright 2014 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 cache |
| |
| import ( |
| "fmt" |
| "strings" |
| |
| "k8s.io/apimachinery/pkg/api/meta" |
| ) |
| |
| // Store is a generic object storage interface. Reflector knows how to watch a server |
| // and update a store. A generic store is provided, which allows Reflector to be used |
| // as a local caching system, and an LRU store, which allows Reflector to work like a |
| // queue of items yet to be processed. |
| // |
| // Store makes no assumptions about stored object identity; it is the responsibility |
| // of a Store implementation to provide a mechanism to correctly key objects and to |
| // define the contract for obtaining objects by some arbitrary key type. |
| type Store interface { |
| Add(obj interface{}) error |
| Update(obj interface{}) error |
| Delete(obj interface{}) error |
| List() []interface{} |
| ListKeys() []string |
| Get(obj interface{}) (item interface{}, exists bool, err error) |
| GetByKey(key string) (item interface{}, exists bool, err error) |
| |
| // Replace will delete the contents of the store, using instead the |
| // given list. Store takes ownership of the list, you should not reference |
| // it after calling this function. |
| Replace([]interface{}, string) error |
| Resync() error |
| } |
| |
| // KeyFunc knows how to make a key from an object. Implementations should be deterministic. |
| type KeyFunc func(obj interface{}) (string, error) |
| |
| // KeyError will be returned any time a KeyFunc gives an error; it includes the object |
| // at fault. |
| type KeyError struct { |
| Obj interface{} |
| Err error |
| } |
| |
| // Error gives a human-readable description of the error. |
| func (k KeyError) Error() string { |
| return fmt.Sprintf("couldn't create key for object %+v: %v", k.Obj, k.Err) |
| } |
| |
| // ExplicitKey can be passed to MetaNamespaceKeyFunc if you have the key for |
| // the object but not the object itself. |
| type ExplicitKey string |
| |
| // MetaNamespaceKeyFunc is a convenient default KeyFunc which knows how to make |
| // keys for API objects which implement meta.Interface. |
| // The key uses the format <namespace>/<name> unless <namespace> is empty, then |
| // it's just <name>. |
| // |
| // TODO: replace key-as-string with a key-as-struct so that this |
| // packing/unpacking won't be necessary. |
| func MetaNamespaceKeyFunc(obj interface{}) (string, error) { |
| if key, ok := obj.(ExplicitKey); ok { |
| return string(key), nil |
| } |
| meta, err := meta.Accessor(obj) |
| if err != nil { |
| return "", fmt.Errorf("object has no meta: %v", err) |
| } |
| if len(meta.GetNamespace()) > 0 { |
| return meta.GetNamespace() + "/" + meta.GetName(), nil |
| } |
| return meta.GetName(), nil |
| } |
| |
| // SplitMetaNamespaceKey returns the namespace and name that |
| // MetaNamespaceKeyFunc encoded into key. |
| // |
| // TODO: replace key-as-string with a key-as-struct so that this |
| // packing/unpacking won't be necessary. |
| func SplitMetaNamespaceKey(key string) (namespace, name string, err error) { |
| parts := strings.Split(key, "/") |
| switch len(parts) { |
| case 1: |
| // name only, no namespace |
| return "", parts[0], nil |
| case 2: |
| // namespace and name |
| return parts[0], parts[1], nil |
| } |
| |
| return "", "", fmt.Errorf("unexpected key format: %q", key) |
| } |
| |
| // cache responsibilities are limited to: |
| // 1. Computing keys for objects via keyFunc |
| // 2. Invoking methods of a ThreadSafeStorage interface |
| type cache struct { |
| // cacheStorage bears the burden of thread safety for the cache |
| cacheStorage ThreadSafeStore |
| // keyFunc is used to make the key for objects stored in and retrieved from items, and |
| // should be deterministic. |
| keyFunc KeyFunc |
| } |
| |
| var _ Store = &cache{} |
| |
| // Add inserts an item into the cache. |
| func (c *cache) Add(obj interface{}) error { |
| key, err := c.keyFunc(obj) |
| if err != nil { |
| return KeyError{obj, err} |
| } |
| c.cacheStorage.Add(key, obj) |
| return nil |
| } |
| |
| // Update sets an item in the cache to its updated state. |
| func (c *cache) Update(obj interface{}) error { |
| key, err := c.keyFunc(obj) |
| if err != nil { |
| return KeyError{obj, err} |
| } |
| c.cacheStorage.Update(key, obj) |
| return nil |
| } |
| |
| // Delete removes an item from the cache. |
| func (c *cache) Delete(obj interface{}) error { |
| key, err := c.keyFunc(obj) |
| if err != nil { |
| return KeyError{obj, err} |
| } |
| c.cacheStorage.Delete(key) |
| return nil |
| } |
| |
| // List returns a list of all the items. |
| // List is completely threadsafe as long as you treat all items as immutable. |
| func (c *cache) List() []interface{} { |
| return c.cacheStorage.List() |
| } |
| |
| // ListKeys returns a list of all the keys of the objects currently |
| // in the cache. |
| func (c *cache) ListKeys() []string { |
| return c.cacheStorage.ListKeys() |
| } |
| |
| // GetIndexers returns the indexers of cache |
| func (c *cache) GetIndexers() Indexers { |
| return c.cacheStorage.GetIndexers() |
| } |
| |
| // Index returns a list of items that match on the index function |
| // Index is thread-safe so long as you treat all items as immutable |
| func (c *cache) Index(indexName string, obj interface{}) ([]interface{}, error) { |
| return c.cacheStorage.Index(indexName, obj) |
| } |
| |
| func (c *cache) IndexKeys(indexName, indexKey string) ([]string, error) { |
| return c.cacheStorage.IndexKeys(indexName, indexKey) |
| } |
| |
| // ListIndexFuncValues returns the list of generated values of an Index func |
| func (c *cache) ListIndexFuncValues(indexName string) []string { |
| return c.cacheStorage.ListIndexFuncValues(indexName) |
| } |
| |
| func (c *cache) ByIndex(indexName, indexKey string) ([]interface{}, error) { |
| return c.cacheStorage.ByIndex(indexName, indexKey) |
| } |
| |
| func (c *cache) AddIndexers(newIndexers Indexers) error { |
| return c.cacheStorage.AddIndexers(newIndexers) |
| } |
| |
| // Get returns the requested item, or sets exists=false. |
| // Get is completely threadsafe as long as you treat all items as immutable. |
| func (c *cache) Get(obj interface{}) (item interface{}, exists bool, err error) { |
| key, err := c.keyFunc(obj) |
| if err != nil { |
| return nil, false, KeyError{obj, err} |
| } |
| return c.GetByKey(key) |
| } |
| |
| // GetByKey returns the request item, or exists=false. |
| // GetByKey is completely threadsafe as long as you treat all items as immutable. |
| func (c *cache) GetByKey(key string) (item interface{}, exists bool, err error) { |
| item, exists = c.cacheStorage.Get(key) |
| return item, exists, nil |
| } |
| |
| // Replace will delete the contents of 'c', using instead the given list. |
| // 'c' takes ownership of the list, you should not reference the list again |
| // after calling this function. |
| func (c *cache) Replace(list []interface{}, resourceVersion string) error { |
| items := make(map[string]interface{}, len(list)) |
| for _, item := range list { |
| key, err := c.keyFunc(item) |
| if err != nil { |
| return KeyError{item, err} |
| } |
| items[key] = item |
| } |
| c.cacheStorage.Replace(items, resourceVersion) |
| return nil |
| } |
| |
| // Resync touches all items in the store to force processing |
| func (c *cache) Resync() error { |
| return c.cacheStorage.Resync() |
| } |
| |
| // NewStore returns a Store implemented simply with a map and a lock. |
| func NewStore(keyFunc KeyFunc) Store { |
| return &cache{ |
| cacheStorage: NewThreadSafeStore(Indexers{}, Indices{}), |
| keyFunc: keyFunc, |
| } |
| } |
| |
| // NewIndexer returns an Indexer implemented simply with a map and a lock. |
| func NewIndexer(keyFunc KeyFunc, indexers Indexers) Indexer { |
| return &cache{ |
| cacheStorage: NewThreadSafeStore(indexers, Indices{}), |
| keyFunc: keyFunc, |
| } |
| } |