github.com/zhyoulun/cilium@v1.6.12/pkg/kvstore/store/store.go (about) 1 // Copyright 2018-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package store 16 17 import ( 18 "context" 19 "fmt" 20 "path" 21 "strings" 22 "time" 23 24 "github.com/cilium/cilium/pkg/controller" 25 "github.com/cilium/cilium/pkg/defaults" 26 "github.com/cilium/cilium/pkg/kvstore" 27 "github.com/cilium/cilium/pkg/lock" 28 "github.com/cilium/cilium/pkg/logging" 29 "github.com/cilium/cilium/pkg/logging/logfields" 30 "github.com/cilium/cilium/pkg/option" 31 32 "github.com/sirupsen/logrus" 33 ) 34 35 const ( 36 // listTimeoutDefault is the default timeout to wait while performing 37 // the initial list operation of objects from the kvstore 38 listTimeoutDefault = 3 * time.Minute 39 40 // watcherChanSize is the size of the channel to buffer kvstore events 41 watcherChanSize = 100 42 ) 43 44 var ( 45 controllers controller.Manager 46 47 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "shared-store") 48 ) 49 50 // KeyCreator is the function to create a new empty Key instances. Store 51 // collaborators must implement this interface and provide the implementation 52 // in the Configuration structure. 53 type KeyCreator func() Key 54 55 // Configuration is the set of configuration parameters of a shared store. 56 type Configuration struct { 57 // Prefix is the key prefix of the store shared by all keys. The prefix 58 // is the unique identification of the store. Multiple collaborators 59 // connected to the same kvstore cluster configuring stores with 60 // matching prefixes will automatically form a shared store. This 61 // parameter is required. 62 Prefix string 63 64 // SynchronizationInterval is the interval in which locally owned keys 65 // are synchronized with the kvstore. This parameter is optional. 66 SynchronizationInterval time.Duration 67 68 // KeyCreator is called to allocate a Key instance when a new shared 69 // key is discovered. This parameter is required. 70 KeyCreator KeyCreator 71 72 // Backend is the kvstore to use as a backend. If no backend is 73 // specified, kvstore.Client() is being used. 74 Backend kvstore.BackendOperations 75 76 // Observer is the observe that will receive events on key mutations 77 Observer Observer 78 } 79 80 // validate is invoked by JoinSharedStore to validate and complete the 81 // configuration. It returns nil when the configuration is valid. 82 func (c *Configuration) validate() error { 83 if c.Prefix == "" { 84 return fmt.Errorf("prefix must be specified") 85 } 86 87 if c.KeyCreator == nil { 88 return fmt.Errorf("KeyCreator must be specified") 89 } 90 91 if c.SynchronizationInterval == 0 { 92 c.SynchronizationInterval = option.Config.KVstorePeriodicSync 93 } 94 95 if c.Backend == nil { 96 c.Backend = kvstore.Client() 97 } 98 99 return nil 100 } 101 102 // SharedStore is an instance of a shared store. It is created with 103 // JoinSharedStore() and released with the SharedStore.Close() function. 104 type SharedStore struct { 105 // conf is a copy of the store configuration. This field is never 106 // mutated after JoinSharedStore() so it is safe to access this without 107 // a lock. 108 conf Configuration 109 110 // name is the name of the shared store. It is derived from the kvstore 111 // prefix. 112 name string 113 114 // controllerName is the name of the controller used to synchronize 115 // with the kvstore. It is derived from the name. 116 controllerName string 117 118 // backend is the backend as configured via Configuration 119 backend kvstore.BackendOperations 120 121 // mutex protects mutations to localKeys and sharedKeys 122 mutex lock.RWMutex 123 124 // localKeys is a map of keys that are owned by the local instance. All 125 // local keys are synchronized with the kvstore. This map can be 126 // modified with UpdateLocalKey() and DeleteLocalKey(). 127 localKeys map[string]LocalKey 128 129 // sharedKeys is a map of all keys that either have been discovered 130 // from remote collaborators or successfully shared local keys. This 131 // map represents the state in the kvstore and is updated based on 132 // kvstore events. 133 sharedKeys map[string]Key 134 135 kvstoreWatcher *kvstore.Watcher 136 } 137 138 // Observer receives events when objects in the store mutate 139 type Observer interface { 140 // OnDelete is called when the key has been deleted from the shared store 141 OnDelete(k NamedKey) 142 143 // OnUpdate is called whenever a change has occurred in the data 144 // structure represented by the key 145 OnUpdate(k Key) 146 } 147 148 // NamedKey is an interface that a data structure must implement in order to 149 // be deleted from a SharedStore. 150 type NamedKey interface { 151 // GetKeyName must return the name of the key. The name of the key must 152 // be unique within the store and stable for a particular key. The name 153 // of the key must be identical across agent restarts as the keys 154 // remain in the kvstore. 155 GetKeyName() string 156 } 157 158 // Key is the interface that a data structure must implement in order to be 159 // stored and shared as a key in a SharedStore. 160 type Key interface { 161 NamedKey 162 163 // Marshal is called to retrieve the byte slice representation of the 164 // data represented by the key to store it in the kvstore. The function 165 // must ensure that the underlying datatype is properly locked. It is 166 // typically a good idea to use json.Marshal to implement this 167 // function. 168 Marshal() ([]byte, error) 169 170 // Unmarshal is called when an update from the kvstore is received. The 171 // byte slice passed to the function is coming from the Marshal 172 // function from another collaborator. The function must unmarshal and 173 // update the underlying data type. It is typically a good idea to use 174 // json.Unmarshal to implement this function. 175 Unmarshal(data []byte) error 176 } 177 178 // LocalKey is a Key owned by the local store instance 179 type LocalKey interface { 180 Key 181 182 // DeepKeyCopy must return a deep copy of the key 183 DeepKeyCopy() LocalKey 184 } 185 186 // JoinSharedStore creates a new shared store based on the provided 187 // configuration. An error is returned if the configuration is invalid. The 188 // store is initialized with the contents of the kvstore. An error is returned 189 // if the contents cannot be retrieved synchronously from the kvstore. Starts a 190 // controller to continuously synchronize the store with the kvstore. 191 func JoinSharedStore(c Configuration) (*SharedStore, error) { 192 if err := c.validate(); err != nil { 193 return nil, err 194 } 195 196 s := &SharedStore{ 197 conf: c, 198 localKeys: map[string]LocalKey{}, 199 sharedKeys: map[string]Key{}, 200 backend: c.Backend, 201 } 202 203 s.name = "store-" + s.conf.Prefix 204 s.controllerName = "kvstore-sync-" + s.name 205 206 if err := s.listAndStartWatcher(); err != nil { 207 return nil, err 208 } 209 210 controllers.UpdateController(s.controllerName, 211 controller.ControllerParams{ 212 DoFunc: func(ctx context.Context) error { 213 return s.syncLocalKeys() 214 }, 215 RunInterval: s.conf.SynchronizationInterval, 216 }, 217 ) 218 219 return s, nil 220 } 221 222 func (s *SharedStore) onDelete(k NamedKey) { 223 if s.conf.Observer != nil { 224 s.conf.Observer.OnDelete(k) 225 } 226 } 227 228 func (s *SharedStore) onUpdate(k Key) { 229 if s.conf.Observer != nil { 230 s.conf.Observer.OnUpdate(k) 231 } 232 } 233 234 // Release frees all resources own by the store but leaves all keys in the 235 // kvstore intact 236 func (s *SharedStore) Release() { 237 // Wait for all write operations to complete and then block all further 238 // operations 239 s.mutex.Lock() 240 defer s.mutex.Unlock() 241 242 if s.kvstoreWatcher != nil { 243 s.kvstoreWatcher.Stop() 244 } 245 246 controllers.RemoveController(s.controllerName) 247 } 248 249 // Close stops participation with a shared store and removes all keys owned by 250 // this node in the kvstore. This stops the controller started by 251 // JoinSharedStore(). 252 func (s *SharedStore) Close() { 253 s.Release() 254 255 for name, key := range s.localKeys { 256 if err := s.backend.Delete(s.keyPath(key)); err != nil { 257 s.getLogger().WithError(err).Warning("Unable to delete key in kvstore") 258 } 259 260 delete(s.localKeys, name) 261 // Since we have received our own notification we also need to remove 262 // it from the shared keys. 263 delete(s.sharedKeys, name) 264 265 s.onDelete(key) 266 } 267 } 268 269 // keyPath returns the absolute kvstore path of a key 270 func (s *SharedStore) keyPath(key NamedKey) string { 271 // WARNING - STABLE API: The composition of the absolute key path 272 // cannot be changed without breaking up and downgrades. 273 return path.Join(s.conf.Prefix, key.GetKeyName()) 274 } 275 276 // syncLocalKey synchronizes a key to the kvstore 277 func (s *SharedStore) syncLocalKey(key LocalKey) error { 278 jsonValue, err := key.Marshal() 279 if err != nil { 280 return err 281 } 282 283 // Update key in kvstore, overwrite an eventual existing key, attach 284 // lease to expire entry when agent dies and never comes back up. 285 if _, err := s.backend.UpdateIfDifferent(context.TODO(), s.keyPath(key), jsonValue, true); err != nil { 286 return err 287 } 288 289 return nil 290 } 291 292 // syncLocalKeys synchronizes all local keys with the kvstore 293 func (s *SharedStore) syncLocalKeys() error { 294 // Create a copy of all local keys so we can unlock and sync to kvstore 295 // without holding the lock 296 s.mutex.RLock() 297 keys := []LocalKey{} 298 for _, key := range s.localKeys { 299 keys = append(keys, key) 300 } 301 s.mutex.RUnlock() 302 303 for _, key := range keys { 304 if err := s.syncLocalKey(key); err != nil { 305 return err 306 } 307 } 308 309 return nil 310 } 311 312 func (s *SharedStore) lookupLocalKey(name string) LocalKey { 313 s.mutex.RLock() 314 defer s.mutex.RUnlock() 315 316 for _, key := range s.localKeys { 317 if key.GetKeyName() == name { 318 return key 319 } 320 } 321 322 return nil 323 } 324 325 // SharedKeysMap returns a copy of the SharedKeysMap, the returned map can 326 // be safely modified but the values of the map represent the actual data 327 // stored in the internal SharedStore SharedKeys map. 328 func (s *SharedStore) SharedKeysMap() map[string]Key { 329 s.mutex.RLock() 330 defer s.mutex.RUnlock() 331 sharedKeysCopy := make(map[string]Key, len(s.sharedKeys)) 332 333 for k, v := range s.sharedKeys { 334 sharedKeysCopy[k] = v 335 } 336 return sharedKeysCopy 337 } 338 339 // UpdateLocalKey adds a key to be synchronized with the kvstore 340 func (s *SharedStore) UpdateLocalKey(key LocalKey) { 341 s.mutex.Lock() 342 s.localKeys[key.GetKeyName()] = key.DeepKeyCopy() 343 s.mutex.Unlock() 344 } 345 346 // UpdateLocalKeySync synchronously synchronizes a local key with the kvstore 347 // and adds it to the list of local keys to be synchronized if the initial 348 // synchronous synchronization was successful 349 func (s *SharedStore) UpdateLocalKeySync(key LocalKey) error { 350 s.mutex.Lock() 351 defer s.mutex.Unlock() 352 err := s.syncLocalKey(key) 353 if err == nil { 354 s.localKeys[key.GetKeyName()] = key.DeepKeyCopy() 355 } 356 return err 357 } 358 359 // UpdateKeySync synchronously synchronizes a key with the kvstore. 360 func (s *SharedStore) UpdateKeySync(key LocalKey) error { 361 return s.syncLocalKey(key) 362 } 363 364 // DeleteLocalKey removes a key from being synchronized with the kvstore 365 func (s *SharedStore) DeleteLocalKey(key NamedKey) { 366 name := key.GetKeyName() 367 368 s.mutex.Lock() 369 _, ok := s.localKeys[name] 370 delete(s.localKeys, name) 371 s.mutex.Unlock() 372 373 err := s.backend.Delete(s.keyPath(key)) 374 375 if ok { 376 if err != nil { 377 s.getLogger().WithError(err).Warning("Unable to delete key in kvstore") 378 } 379 380 s.onDelete(key) 381 } 382 } 383 384 // getLocalKeys returns all local keys 385 func (s *SharedStore) getLocalKeys() []Key { 386 s.mutex.RLock() 387 defer s.mutex.RUnlock() 388 389 keys := make([]Key, len(s.localKeys)) 390 idx := 0 391 for _, key := range s.localKeys { 392 keys[idx] = key 393 idx++ 394 } 395 396 return keys 397 } 398 399 // getSharedKeys returns all shared keys 400 func (s *SharedStore) getSharedKeys() []Key { 401 s.mutex.RLock() 402 defer s.mutex.RUnlock() 403 404 keys := make([]Key, len(s.sharedKeys)) 405 idx := 0 406 for _, key := range s.sharedKeys { 407 keys[idx] = key 408 idx++ 409 } 410 411 return keys 412 } 413 414 func (s *SharedStore) getLogger() *logrus.Entry { 415 return log.WithFields(logrus.Fields{ 416 "storeName": s.name, 417 }) 418 } 419 420 func (s *SharedStore) updateKey(name string, value []byte) error { 421 newKey := s.conf.KeyCreator() 422 if err := newKey.Unmarshal(value); err != nil { 423 return err 424 } 425 426 s.mutex.Lock() 427 s.sharedKeys[name] = newKey 428 s.mutex.Unlock() 429 430 s.onUpdate(newKey) 431 return nil 432 } 433 434 func (s *SharedStore) deleteSharedKey(name string) { 435 s.mutex.Lock() 436 existingKey, ok := s.sharedKeys[name] 437 delete(s.sharedKeys, name) 438 s.mutex.Unlock() 439 440 if ok { 441 go func() { 442 time.Sleep(defaults.NodeDeleteDelay) 443 s.mutex.RLock() 444 _, ok := s.sharedKeys[name] 445 s.mutex.RUnlock() 446 if ok { 447 log.Warningf("Received node delete event for node %s which re-appeared within %s", 448 name, defaults.NodeDeleteDelay) 449 return 450 } 451 452 s.onDelete(existingKey) 453 }() 454 } else { 455 s.getLogger().WithField("key", name). 456 Warning("Unable to find deleted key in local state") 457 } 458 } 459 460 func (s *SharedStore) listAndStartWatcher() error { 461 listDone := make(chan bool) 462 463 go s.watcher(listDone) 464 465 select { 466 case <-listDone: 467 case <-time.After(listTimeoutDefault): 468 return fmt.Errorf("timeout while retrieving initial list of objects from kvstore") 469 } 470 471 return nil 472 } 473 474 func (s *SharedStore) watcher(listDone chan bool) { 475 s.kvstoreWatcher = s.backend.ListAndWatch(s.name+"-watcher", s.conf.Prefix, watcherChanSize) 476 477 for event := range s.kvstoreWatcher.Events { 478 if event.Typ == kvstore.EventTypeListDone { 479 s.getLogger().Debug("Initial list of objects received from kvstore") 480 close(listDone) 481 continue 482 } 483 484 logger := s.getLogger().WithFields(logrus.Fields{ 485 "key": event.Key, 486 "eventType": event.Typ, 487 }) 488 489 logger.Debugf("Received key update via kvstore [value %s]", string(event.Value)) 490 491 keyName := strings.TrimPrefix(event.Key, s.conf.Prefix) 492 if keyName[0] == '/' { 493 keyName = keyName[1:] 494 } 495 496 switch event.Typ { 497 case kvstore.EventTypeCreate, kvstore.EventTypeModify: 498 if err := s.updateKey(keyName, event.Value); err != nil { 499 logger.WithError(err).Warningf("Unable to unmarshal store value: %s", string(event.Value)) 500 } 501 502 case kvstore.EventTypeDelete: 503 if localKey := s.lookupLocalKey(keyName); localKey != nil { 504 logger.Warning("Received delete event for local key. Re-creating the key in the kvstore") 505 506 s.syncLocalKey(localKey) 507 } else { 508 s.deleteSharedKey(keyName) 509 } 510 } 511 } 512 }