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 }