1 /* 2 Copyright 2014 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 cache 18 19 import ( 20 "fmt" 21 "strings" 22 23 "k8s.io/apimachinery/pkg/api/meta" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 ) 26 27 // Store is a generic object storage and processing interface. A 28 // Store holds a map from string keys to accumulators, and has 29 // operations to add, update, and delete a given object to/from the 30 // accumulator currently associated with a given key. A Store also 31 // knows how to extract the key from a given object, so many operations 32 // are given only the object. 33 // 34 // In the simplest Store implementations each accumulator is simply 35 // the last given object, or empty after Delete, and thus the Store's 36 // behavior is simple storage. 37 // 38 // Reflector knows how to watch a server and update a Store. This 39 // package provides a variety of implementations of Store. 40 type Store interface { 41 42 // Add adds the given object to the accumulator associated with the given object's key 43 Add(obj interface{}) error 44 45 // Update updates the given object in the accumulator associated with the given object's key 46 Update(obj interface{}) error 47 48 // Delete deletes the given object from the accumulator associated with the given object's key 49 Delete(obj interface{}) error 50 51 // List returns a list of all the currently non-empty accumulators 52 List() []interface{} 53 54 // ListKeys returns a list of all the keys currently associated with non-empty accumulators 55 ListKeys() []string 56 57 // Get returns the accumulator associated with the given object's key 58 Get(obj interface{}) (item interface{}, exists bool, err error) 59 60 // GetByKey returns the accumulator associated with the given key 61 GetByKey(key string) (item interface{}, exists bool, err error) 62 63 // Replace will delete the contents of the store, using instead the 64 // given list. Store takes ownership of the list, you should not reference 65 // it after calling this function. 66 Replace([]interface{}, string) error 67 68 // Resync is meaningless in the terms appearing here but has 69 // meaning in some implementations that have non-trivial 70 // additional behavior (e.g., DeltaFIFO). 71 Resync() error 72 } 73 74 // KeyFunc knows how to make a key from an object. Implementations should be deterministic. 75 type KeyFunc func(obj interface{}) (string, error) 76 77 // KeyError will be returned any time a KeyFunc gives an error; it includes the object 78 // at fault. 79 type KeyError struct { 80 Obj interface{} 81 Err error 82 } 83 84 // Error gives a human-readable description of the error. 85 func (k KeyError) Error() string { 86 return fmt.Sprintf("couldn't create key for object %+v: %v", k.Obj, k.Err) 87 } 88 89 // Unwrap implements errors.Unwrap 90 func (k KeyError) Unwrap() error { 91 return k.Err 92 } 93 94 // ExplicitKey can be passed to MetaNamespaceKeyFunc if you have the key for 95 // the object but not the object itself. 96 type ExplicitKey string 97 98 // MetaNamespaceKeyFunc is a convenient default KeyFunc which knows how to make 99 // keys for API objects which implement meta.Interface. 100 // The key uses the format <namespace>/<name> unless <namespace> is empty, then 101 // it's just <name>. 102 // 103 // Clients that want a structured alternative can use ObjectToName or MetaObjectToName. 104 // Note: this would not be a client that wants a key for a Store because those are 105 // necessarily strings. 106 // 107 // TODO maybe some day?: change Store to be keyed differently 108 func MetaNamespaceKeyFunc(obj interface{}) (string, error) { 109 if key, ok := obj.(ExplicitKey); ok { 110 return string(key), nil 111 } 112 objName, err := ObjectToName(obj) 113 if err != nil { 114 return "", err 115 } 116 return objName.String(), nil 117 } 118 119 // ObjectToName returns the structured name for the given object, 120 // if indeed it can be viewed as a metav1.Object. 121 func ObjectToName(obj interface{}) (ObjectName, error) { 122 meta, err := meta.Accessor(obj) 123 if err != nil { 124 return ObjectName{}, fmt.Errorf("object has no meta: %v", err) 125 } 126 return MetaObjectToName(meta), nil 127 } 128 129 // MetaObjectToName returns the structured name for the given object 130 func MetaObjectToName(obj metav1.Object) ObjectName { 131 if len(obj.GetNamespace()) > 0 { 132 return ObjectName{Namespace: obj.GetNamespace(), Name: obj.GetName()} 133 } 134 return ObjectName{Namespace: "", Name: obj.GetName()} 135 } 136 137 // SplitMetaNamespaceKey returns the namespace and name that 138 // MetaNamespaceKeyFunc encoded into key. 139 // 140 // TODO: replace key-as-string with a key-as-struct so that this 141 // packing/unpacking won't be necessary. 142 func SplitMetaNamespaceKey(key string) (namespace, name string, err error) { 143 parts := strings.Split(key, "/") 144 switch len(parts) { 145 case 1: 146 // name only, no namespace 147 return "", parts[0], nil 148 case 2: 149 // namespace and name 150 return parts[0], parts[1], nil 151 } 152 153 return "", "", fmt.Errorf("unexpected key format: %q", key) 154 } 155 156 // `*cache` implements Indexer in terms of a ThreadSafeStore and an 157 // associated KeyFunc. 158 type cache struct { 159 // cacheStorage bears the burden of thread safety for the cache 160 cacheStorage ThreadSafeStore 161 // keyFunc is used to make the key for objects stored in and retrieved from items, and 162 // should be deterministic. 163 keyFunc KeyFunc 164 } 165 166 var _ Store = &cache{} 167 168 // Add inserts an item into the cache. 169 func (c *cache) Add(obj interface{}) error { 170 key, err := c.keyFunc(obj) 171 if err != nil { 172 return KeyError{obj, err} 173 } 174 c.cacheStorage.Add(key, obj) 175 return nil 176 } 177 178 // Update sets an item in the cache to its updated state. 179 func (c *cache) Update(obj interface{}) error { 180 key, err := c.keyFunc(obj) 181 if err != nil { 182 return KeyError{obj, err} 183 } 184 c.cacheStorage.Update(key, obj) 185 return nil 186 } 187 188 // Delete removes an item from the cache. 189 func (c *cache) Delete(obj interface{}) error { 190 key, err := c.keyFunc(obj) 191 if err != nil { 192 return KeyError{obj, err} 193 } 194 c.cacheStorage.Delete(key) 195 return nil 196 } 197 198 // List returns a list of all the items. 199 // List is completely threadsafe as long as you treat all items as immutable. 200 func (c *cache) List() []interface{} { 201 return c.cacheStorage.List() 202 } 203 204 // ListKeys returns a list of all the keys of the objects currently 205 // in the cache. 206 func (c *cache) ListKeys() []string { 207 return c.cacheStorage.ListKeys() 208 } 209 210 // GetIndexers returns the indexers of cache 211 func (c *cache) GetIndexers() Indexers { 212 return c.cacheStorage.GetIndexers() 213 } 214 215 // Index returns a list of items that match on the index function 216 // Index is thread-safe so long as you treat all items as immutable 217 func (c *cache) Index(indexName string, obj interface{}) ([]interface{}, error) { 218 return c.cacheStorage.Index(indexName, obj) 219 } 220 221 // IndexKeys returns the storage keys of the stored objects whose set of 222 // indexed values for the named index includes the given indexed value. 223 // The returned keys are suitable to pass to GetByKey(). 224 func (c *cache) IndexKeys(indexName, indexedValue string) ([]string, error) { 225 return c.cacheStorage.IndexKeys(indexName, indexedValue) 226 } 227 228 // ListIndexFuncValues returns the list of generated values of an Index func 229 func (c *cache) ListIndexFuncValues(indexName string) []string { 230 return c.cacheStorage.ListIndexFuncValues(indexName) 231 } 232 233 // ByIndex returns the stored objects whose set of indexed values 234 // for the named index includes the given indexed value. 235 func (c *cache) ByIndex(indexName, indexedValue string) ([]interface{}, error) { 236 return c.cacheStorage.ByIndex(indexName, indexedValue) 237 } 238 239 func (c *cache) AddIndexers(newIndexers Indexers) error { 240 return c.cacheStorage.AddIndexers(newIndexers) 241 } 242 243 // Get returns the requested item, or sets exists=false. 244 // Get is completely threadsafe as long as you treat all items as immutable. 245 func (c *cache) Get(obj interface{}) (item interface{}, exists bool, err error) { 246 key, err := c.keyFunc(obj) 247 if err != nil { 248 return nil, false, KeyError{obj, err} 249 } 250 return c.GetByKey(key) 251 } 252 253 // GetByKey returns the request item, or exists=false. 254 // GetByKey is completely threadsafe as long as you treat all items as immutable. 255 func (c *cache) GetByKey(key string) (item interface{}, exists bool, err error) { 256 item, exists = c.cacheStorage.Get(key) 257 return item, exists, nil 258 } 259 260 // Replace will delete the contents of 'c', using instead the given list. 261 // 'c' takes ownership of the list, you should not reference the list again 262 // after calling this function. 263 func (c *cache) Replace(list []interface{}, resourceVersion string) error { 264 items := make(map[string]interface{}, len(list)) 265 for _, item := range list { 266 key, err := c.keyFunc(item) 267 if err != nil { 268 return KeyError{item, err} 269 } 270 items[key] = item 271 } 272 c.cacheStorage.Replace(items, resourceVersion) 273 return nil 274 } 275 276 // Resync is meaningless for one of these 277 func (c *cache) Resync() error { 278 return nil 279 } 280 281 // NewStore returns a Store implemented simply with a map and a lock. 282 func NewStore(keyFunc KeyFunc) Store { 283 return &cache{ 284 cacheStorage: NewThreadSafeStore(Indexers{}, Indices{}), 285 keyFunc: keyFunc, 286 } 287 } 288 289 // NewIndexer returns an Indexer implemented simply with a map and a lock. 290 func NewIndexer(keyFunc KeyFunc, indexers Indexers) Indexer { 291 return &cache{ 292 cacheStorage: NewThreadSafeStore(indexers, Indices{}), 293 keyFunc: keyFunc, 294 } 295 } 296