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  }