github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/libnetwork/datastore/datastore.go (about)

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