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 }