github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/datastore/datastore.go (about)

     1  package datastore
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/discoverapi"
    10  	store "github.com/Prakhar-Agarwal-byte/moby/libnetwork/internal/kvstore"
    11  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/internal/kvstore/boltdb"
    12  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/scope"
    13  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/types"
    14  )
    15  
    16  // ErrKeyModified is raised for an atomic update when the update is working on a stale state
    17  var (
    18  	ErrKeyModified = store.ErrKeyModified
    19  	ErrKeyNotFound = store.ErrKeyNotFound
    20  )
    21  
    22  type Store struct {
    23  	mu    sync.Mutex
    24  	scope string
    25  	store store.Store
    26  	cache *cache
    27  }
    28  
    29  // KVObject is Key/Value interface used by objects to be part of the Store.
    30  type KVObject interface {
    31  	// Key method lets an object provide the Key to be used in KV Store
    32  	Key() []string
    33  	// KeyPrefix method lets an object return immediate parent key that can be used for tree walk
    34  	KeyPrefix() []string
    35  	// Value method lets an object marshal its content to be stored in the KV store
    36  	Value() []byte
    37  	// SetValue is used by the datastore to set the object's value when loaded from the data store.
    38  	SetValue([]byte) error
    39  	// Index method returns the latest DB Index as seen by the object
    40  	Index() uint64
    41  	// SetIndex method allows the datastore to store the latest DB Index into the object
    42  	SetIndex(uint64)
    43  	// Exists returns true if the object exists in the datastore, false if it hasn't been stored yet.
    44  	// When SetIndex() is called, the object has been stored.
    45  	Exists() bool
    46  	// DataScope indicates the storage scope of the KV object
    47  	DataScope() string
    48  	// Skip provides a way for a KV Object to avoid persisting it in the KV Store
    49  	Skip() bool
    50  }
    51  
    52  // KVConstructor interface defines methods which can construct a KVObject from another.
    53  type KVConstructor interface {
    54  	// New returns a new object which is created based on the
    55  	// source object
    56  	New() KVObject
    57  	// CopyTo deep copies the contents of the implementing object
    58  	// to the passed destination object
    59  	CopyTo(KVObject) error
    60  }
    61  
    62  // ScopeCfg represents Datastore configuration.
    63  type ScopeCfg struct {
    64  	Client ScopeClientCfg
    65  }
    66  
    67  // ScopeClientCfg represents Datastore Client-only mode configuration
    68  type ScopeClientCfg struct {
    69  	Provider string
    70  	Address  string
    71  	Config   *store.Config
    72  }
    73  
    74  const (
    75  	// NetworkKeyPrefix is the prefix for network key in the kv store
    76  	NetworkKeyPrefix = "network"
    77  	// EndpointKeyPrefix is the prefix for endpoint key in the kv store
    78  	EndpointKeyPrefix = "endpoint"
    79  )
    80  
    81  var (
    82  	defaultRootChain = []string{"docker", "network", "v1.0"}
    83  	rootChain        = defaultRootChain
    84  )
    85  
    86  const defaultPrefix = "/var/lib/docker/network/files"
    87  
    88  // DefaultScope returns a default scope config for clients to use.
    89  func DefaultScope(dataDir string) ScopeCfg {
    90  	var dbpath string
    91  	if dataDir == "" {
    92  		dbpath = defaultPrefix + "/local-kv.db"
    93  	} else {
    94  		dbpath = dataDir + "/network/files/local-kv.db"
    95  	}
    96  
    97  	return ScopeCfg{
    98  		Client: ScopeClientCfg{
    99  			Provider: string(store.BOLTDB),
   100  			Address:  dbpath,
   101  			Config: &store.Config{
   102  				Bucket:            "libnetwork",
   103  				ConnectionTimeout: time.Minute,
   104  			},
   105  		},
   106  	}
   107  }
   108  
   109  // IsValid checks if the scope config has valid configuration.
   110  func (cfg *ScopeCfg) IsValid() bool {
   111  	if cfg == nil || strings.TrimSpace(cfg.Client.Provider) == "" || strings.TrimSpace(cfg.Client.Address) == "" {
   112  		return false
   113  	}
   114  
   115  	return true
   116  }
   117  
   118  // Key provides convenient method to create a Key
   119  func Key(key ...string) string {
   120  	var b strings.Builder
   121  	for _, parts := range [][]string{rootChain, key} {
   122  		for _, part := range parts {
   123  			b.WriteString(part)
   124  			b.WriteString("/")
   125  		}
   126  	}
   127  	return b.String()
   128  }
   129  
   130  // newClient used to connect to KV Store
   131  func newClient(kv string, addr string, config *store.Config) (*Store, error) {
   132  	if kv != string(store.BOLTDB) {
   133  		return nil, fmt.Errorf("unsupported KV store")
   134  	}
   135  
   136  	if config == nil {
   137  		config = &store.Config{}
   138  	}
   139  
   140  	// Parse file path
   141  	s, err := boltdb.New(strings.Split(addr, ","), config)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	ds := &Store{scope: scope.Local, store: s}
   147  	ds.cache = newCache(ds)
   148  
   149  	return ds, nil
   150  }
   151  
   152  // New creates a new Store instance.
   153  func New(cfg ScopeCfg) (*Store, error) {
   154  	if cfg.Client.Provider == "" || cfg.Client.Address == "" {
   155  		cfg = DefaultScope("")
   156  	}
   157  
   158  	return newClient(cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config)
   159  }
   160  
   161  // FromConfig creates a new instance of LibKV data store starting from the datastore config data.
   162  func FromConfig(dsc discoverapi.DatastoreConfigData) (*Store, error) {
   163  	var (
   164  		ok    bool
   165  		sCfgP *store.Config
   166  	)
   167  
   168  	sCfgP, ok = dsc.Config.(*store.Config)
   169  	if !ok && dsc.Config != nil {
   170  		return nil, fmt.Errorf("cannot parse store configuration: %v", dsc.Config)
   171  	}
   172  
   173  	ds, err := New(ScopeCfg{
   174  		Client: ScopeClientCfg{
   175  			Address:  dsc.Address,
   176  			Provider: dsc.Provider,
   177  			Config:   sCfgP,
   178  		},
   179  	})
   180  	if err != nil {
   181  		return nil, fmt.Errorf("failed to construct datastore client from datastore configuration %v: %v", dsc, err)
   182  	}
   183  
   184  	return ds, err
   185  }
   186  
   187  // Close closes the data store.
   188  func (ds *Store) Close() {
   189  	ds.store.Close()
   190  }
   191  
   192  // Scope returns the scope of the store.
   193  func (ds *Store) Scope() string {
   194  	return ds.scope
   195  }
   196  
   197  // PutObjectAtomic provides an atomic add and update operation for a Record.
   198  func (ds *Store) PutObjectAtomic(kvObject KVObject) error {
   199  	var (
   200  		previous *store.KVPair
   201  		pair     *store.KVPair
   202  		err      error
   203  	)
   204  	ds.mu.Lock()
   205  	defer ds.mu.Unlock()
   206  
   207  	if kvObject == nil {
   208  		return types.InvalidParameterErrorf("invalid KV Object : nil")
   209  	}
   210  
   211  	kvObjValue := kvObject.Value()
   212  
   213  	if kvObjValue == nil {
   214  		return types.InvalidParameterErrorf("invalid KV Object with a nil Value for key %s", Key(kvObject.Key()...))
   215  	}
   216  
   217  	if kvObject.Skip() {
   218  		goto add_cache
   219  	}
   220  
   221  	if kvObject.Exists() {
   222  		previous = &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()}
   223  	} else {
   224  		previous = nil
   225  	}
   226  
   227  	pair, err = ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous)
   228  	if err != nil {
   229  		if err == store.ErrKeyExists {
   230  			return ErrKeyModified
   231  		}
   232  		return err
   233  	}
   234  
   235  	kvObject.SetIndex(pair.LastIndex)
   236  
   237  add_cache:
   238  	if ds.cache != nil {
   239  		// If persistent store is skipped, sequencing needs to
   240  		// happen in cache.
   241  		return ds.cache.add(kvObject, kvObject.Skip())
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  // GetObject gets data from the store and unmarshals to the specified object.
   248  func (ds *Store) GetObject(key string, o KVObject) error {
   249  	ds.mu.Lock()
   250  	defer ds.mu.Unlock()
   251  
   252  	if ds.cache != nil {
   253  		return ds.cache.get(o)
   254  	}
   255  
   256  	kvPair, err := ds.store.Get(key)
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	if err := o.SetValue(kvPair.Value); err != nil {
   262  		return err
   263  	}
   264  
   265  	// Make sure the object has a correct view of the DB index in
   266  	// case we need to modify it and update the DB.
   267  	o.SetIndex(kvPair.LastIndex)
   268  	return nil
   269  }
   270  
   271  func (ds *Store) ensureParent(parent string) error {
   272  	exists, err := ds.store.Exists(parent)
   273  	if err != nil {
   274  		return err
   275  	}
   276  	if exists {
   277  		return nil
   278  	}
   279  	return ds.store.Put(parent, []byte{})
   280  }
   281  
   282  // List returns of a list of KVObjects belonging to the parent key. The caller
   283  // must pass a KVObject of the same type as the objects that need to be listed.
   284  func (ds *Store) List(key string, kvObject KVObject) ([]KVObject, error) {
   285  	ds.mu.Lock()
   286  	defer ds.mu.Unlock()
   287  
   288  	if ds.cache != nil {
   289  		return ds.cache.list(kvObject)
   290  	}
   291  
   292  	var kvol []KVObject
   293  	err := ds.iterateKVPairsFromStore(key, kvObject, func(key string, val KVObject) {
   294  		kvol = append(kvol, val)
   295  	})
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	return kvol, nil
   300  }
   301  
   302  func (ds *Store) iterateKVPairsFromStore(key string, kvObject KVObject, callback func(string, KVObject)) error {
   303  	// Bail out right away if the kvObject does not implement KVConstructor
   304  	ctor, ok := kvObject.(KVConstructor)
   305  	if !ok {
   306  		return fmt.Errorf("error listing objects, object does not implement KVConstructor interface")
   307  	}
   308  
   309  	// Make sure the parent key exists
   310  	if err := ds.ensureParent(key); err != nil {
   311  		return err
   312  	}
   313  
   314  	kvList, err := ds.store.List(key)
   315  	if err != nil {
   316  		return err
   317  	}
   318  
   319  	for _, kvPair := range kvList {
   320  		if len(kvPair.Value) == 0 {
   321  			continue
   322  		}
   323  
   324  		dstO := ctor.New()
   325  		if err := dstO.SetValue(kvPair.Value); err != nil {
   326  			return err
   327  		}
   328  
   329  		// Make sure the object has a correct view of the DB index in
   330  		// case we need to modify it and update the DB.
   331  		dstO.SetIndex(kvPair.LastIndex)
   332  		callback(kvPair.Key, dstO)
   333  	}
   334  
   335  	return nil
   336  }
   337  
   338  // Map returns a Map of KVObjects.
   339  func (ds *Store) Map(key string, kvObject KVObject) (map[string]KVObject, error) {
   340  	ds.mu.Lock()
   341  	defer ds.mu.Unlock()
   342  
   343  	results := map[string]KVObject{}
   344  	err := ds.iterateKVPairsFromStore(key, kvObject, func(key string, val KVObject) {
   345  		// Trim the leading & trailing "/" to make it consistent across all stores
   346  		results[strings.Trim(key, "/")] = val
   347  	})
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  	return results, nil
   352  }
   353  
   354  // DeleteObjectAtomic performs atomic delete on a record.
   355  func (ds *Store) DeleteObjectAtomic(kvObject KVObject) error {
   356  	ds.mu.Lock()
   357  	defer ds.mu.Unlock()
   358  
   359  	if kvObject == nil {
   360  		return types.InvalidParameterErrorf("invalid KV Object : nil")
   361  	}
   362  
   363  	previous := &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()}
   364  
   365  	if kvObject.Skip() {
   366  		goto deleteCache
   367  	}
   368  
   369  	if err := ds.store.AtomicDelete(Key(kvObject.Key()...), previous); err != nil {
   370  		if err == store.ErrKeyExists {
   371  			return ErrKeyModified
   372  		}
   373  		return err
   374  	}
   375  
   376  deleteCache:
   377  	// cleanup the cache only if AtomicDelete went through successfully
   378  	if ds.cache != nil {
   379  		// If persistent store is skipped, sequencing needs to
   380  		// happen in cache.
   381  		return ds.cache.del(kvObject, kvObject.Skip())
   382  	}
   383  
   384  	return nil
   385  }