github.com/portworx/kvdb@v0.0.0-20241107215734-a185a966f535/kvdb.go (about) 1 package kvdb 2 3 import ( 4 "errors" 5 "time" 6 7 _ "github.com/golang/mock/mockgen/model" 8 "github.com/sirupsen/logrus" 9 ) 10 11 const ( 12 // KVSet signifies the KV was modified. 13 KVSet KVAction = 1 << iota 14 // KVCreate set if the KV pair was created. 15 KVCreate 16 // KVGet set when the key is fetched from the KV store 17 KVGet 18 // KVDelete set when the key is deleted from the KV store 19 KVDelete 20 // KVExpire set when the key expires 21 KVExpire 22 // KVUknown operation on KV pair 23 KVUknown 24 ) 25 26 const ( 27 // KVCapabilityOrderedUpdates support requires watch to send an watch update 28 // for every put - instead of coalescing multiple puts in one update. 29 KVCapabilityOrderedUpdates = 1 << iota 30 ) 31 32 const ( 33 // KVPrevExists flag to check key already exists 34 KVPrevExists KVFlags = 1 << iota 35 // KVCreatedIndex flag compares with passed in index (possibly in KVPair) 36 KVCreatedIndex 37 // KVModifiedIndex flag compares with passed in index (possibly in KVPair) 38 KVModifiedIndex 39 // KVTTL uses TTL val from KVPair. 40 KVTTL 41 ) 42 43 const ( 44 // ReadPermission for read only access 45 ReadPermission = iota 46 // WritePermission for write only access 47 WritePermission 48 // ReadWritePermission for read-write access 49 ReadWritePermission 50 ) 51 const ( 52 // UsernameKey for an authenticated kvdb endpoint 53 UsernameKey = "Username" 54 // PasswordKey for an authenticated kvdb endpoint 55 PasswordKey = "Password" 56 // CAFileKey is the CA file path for an authenticated kvdb endpoint 57 CAFileKey = "CAFile" 58 // CertFileKey is the certificate file path for an authenticated kvdb endpoint 59 CertFileKey = "CertFile" 60 // CertKeyFileKey is the key to the certificate 61 CertKeyFileKey = "CertKeyFile" 62 // TrustedCAFileKey is the key for the trusted CA. 63 TrustedCAFileKey = "TrustedCAFile" 64 // ClientCertAuthKey is the boolean value indicating client authenticated certificate. 65 ClientCertAuthKey = "ClientCertAuth" 66 // RetryCountKey is the integer value indicating the retry count of etcd operations 67 RetryCountKey = "RetryCount" 68 // ACLTokenKey is the token value for ACL based KV stores 69 ACLTokenKey = "ACLToken" 70 // CAAuthAddress is the address of CA signing authority (required in consul TLS config) 71 CAAuthAddress = "CAAuthAddress" 72 // InsecureSkipVerify has a value true or false (required in consul TLS config) 73 InsecureSkipVerify = "InsecureSkipVerify" 74 // TransportScheme points to http transport being either http or https. 75 TransportScheme = "TransportScheme" 76 // LogPathOption is the name of the option which specified the log path location 77 LogPathOption = "LogPathOption" 78 ) 79 80 // List of kvdb endpoints supported versions 81 const ( 82 // ConsulVersion1 key 83 ConsulVersion1 = "consulv1" 84 // EtcdBaseVersion key 85 EtcdBaseVersion = "etcd" 86 // EtcdVersion3 key 87 EtcdVersion3 = "etcdv3" 88 // MemVersion1 key 89 MemVersion1 = "memv1" 90 // BoltVersion1 key 91 BoltVersion1 = "boltv1" 92 // ZookeeperVersion1 key 93 ZookeeperVersion1 = "zookeeperv1" 94 ) 95 96 const ( 97 // DefaultLockTryDuration is the maximum time spent trying to acquire lock 98 DefaultLockTryDuration = 300 * time.Second 99 // DefaultSeparator separate key components 100 DefaultSeparator = "/" 101 ) 102 103 type WrapperName string 104 105 const ( 106 Wrapper_None = WrapperName("Wrapper_None") 107 Wrapper_Log = WrapperName("Wrapper_Log") 108 Wrapper_NoQuorum = WrapperName("Wrapper_NoQuorum") 109 ) 110 111 type KvdbWrapper interface { 112 // WrapperName is the name of this wrapper 113 WrapperName() WrapperName 114 // WrappedKvdb is the Kvdb wrapped by this wrapper 115 WrappedKvdb() Kvdb 116 // Removed is called when wrapper is removed 117 Removed() 118 // WrappedKvdb is the Kvdb wrapped by this wrapper 119 SetWrappedKvdb(kvdb Kvdb) error 120 } 121 122 var ( 123 // ErrNotSupported implemenation of a specific function is not supported. 124 ErrNotSupported = errors.New("implementation not supported") 125 // ErrWatchStopped is raised when user stops watch. 126 ErrWatchStopped = errors.New("Watch Stopped") 127 // ErrNotFound raised if Key is not found 128 ErrNotFound = errors.New("Key not found") 129 // ErrExist raised if key already exists 130 ErrExist = errors.New("Key already exists") 131 // ErrUnmarshal raised if Get fails to unmarshal value. 132 ErrUnmarshal = errors.New("Failed to unmarshal value") 133 // ErrIllegal raised if object is not valid. 134 ErrIllegal = errors.New("Illegal operation") 135 // ErrValueMismatch raised if existing KVDB value mismatches with user provided value 136 ErrValueMismatch = errors.New("Value mismatch") 137 // ErrEmptyValue raised if the value is empty 138 ErrEmptyValue = errors.New("Value cannot be empty") 139 // ErrModified raised during an atomic operation if the index does not match the one in the store 140 ErrModified = errors.New("Key Index mismatch") 141 // ErrSetTTLFailed raised if unable to set ttl value for a key create/put/update action 142 ErrSetTTLFailed = errors.New("Unable to set ttl value") 143 // ErrTTLNotSupported if kvdb implementation doesn't support TTL 144 ErrTTLNotSupported = errors.New("TTL value not supported") 145 // ErrInvalidLock Lock and unlock operations don't match. 146 ErrInvalidLock = errors.New("Invalid lock/unlock operation") 147 // ErrNoPassword provided 148 ErrNoPassword = errors.New("Username provided without any password") 149 // ErrAuthNotSupported authentication not supported for this kvdb implementation 150 ErrAuthNotSupported = errors.New("Kvdb authentication not supported") 151 // ErrNoCertificate no certificate provided for authentication 152 ErrNoCertificate = errors.New("Certificate File Path not provided") 153 // ErrUnknownPermission raised if unknown permission type 154 ErrUnknownPermission = errors.New("Unknown Permission Type") 155 // ErrMemberDoesNotExist returned when an operation fails for a member 156 // which does not exist 157 ErrMemberDoesNotExist = errors.New("Kvdb member does not exist") 158 // ErrWatchRevisionCompacted requested watch version has been compacted 159 ErrWatchRevisionCompacted = errors.New("Kvdb watch revision compacted") 160 // ErrLockRefreshFailed could not refresh lock key so exclusive access to lock may be lost 161 ErrLockRefreshFailed = errors.New("Failed to refresh lock") 162 // ErrLockHoldTimeoutTriggered triggers if lock is held beyond configured timeout 163 ErrLockHoldTimeoutTriggered = errors.New("Lock held beyond configured timeout") 164 // ErrNoConnection no connection to server 165 ErrNoConnection = errors.New("No server connection") 166 // ErrNoQuorum kvdb has lost quorum 167 ErrNoQuorum = errors.New("KVDB connection failed, either node has " + 168 "networking issues or KVDB members are down or KVDB cluster is unhealthy. " + 169 "All operations (get/update/delete) are unavailable.") 170 ) 171 172 // KVAction specifies the action on a KV pair. This is useful to make decisions 173 // from the results of a Watch. 174 type KVAction int 175 176 // KVFlags options for operations on KVDB 177 type KVFlags uint64 178 179 // PermissionType for user access 180 type PermissionType int 181 182 // WatchCB is called when a watched key or tree is modified. If the callback 183 // returns an error, then watch stops and the cb is called one last time 184 // with ErrWatchStopped. 185 type WatchCB func(prefix string, opaque interface{}, kvp *KVPair, err error) error 186 187 // FatalErrorCB callback is invoked incase of fatal errors 188 type FatalErrorCB func(err error, format string, args ...interface{}) 189 190 // DatastoreInit is called to activate a backend KV store. 191 type DatastoreInit func(domain string, machines []string, options map[string]string, 192 cb FatalErrorCB) (Kvdb, error) 193 194 // DatastoreVersion is called to get the version of a backend KV store 195 type DatastoreVersion func(url string, kvdbOptions map[string]string) (string, error) 196 197 // WrapperInit is called to activate a backend KV store. 198 type WrapperInit func(kv Kvdb, options map[string]string) (Kvdb, error) 199 200 // EnumerateSelect function is a callback function provided to EnumerateWithSelect API 201 // This fn is executed over all the keys and only those values are returned by Enumerate for which 202 // this function return true. 203 type EnumerateSelect func(val interface{}) bool 204 205 // CopySelect function is a callback function provided to EnumerateWithSelect API 206 // This fn should perform a deep copy of the input interface and return the copy 207 type CopySelect func(val interface{}) interface{} 208 209 // EnumerateKVPSelect function is a callback function provided to EnumerateKVPWithSelect API 210 // This fn is executed over all the keys and only those values are returned by Enumerate for which 211 // this function return true. 212 type EnumerateKVPSelect func(kvp *KVPair, val interface{}) bool 213 214 // CopyKVPSelect function is a callback function provided to EnumerateKVPWithSelect API 215 // This fn should perform a deep copy of the input KVPair and return the copy 216 type CopyKVPSelect func(kvp *KVPair, val interface{}) *KVPair 217 218 // KVPair represents the results of an operation on KVDB. 219 type KVPair struct { 220 // Key for this kv pair. 221 Key string 222 // Value for this kv pair 223 Value []byte 224 // Action the last action on this KVPair. 225 Action KVAction 226 // TTL value after which this key will expire from KVDB 227 TTL int64 228 // KVDBIndex A Monotonically index updated at each modification operation. 229 KVDBIndex uint64 230 // CreatedIndex for this kv pair 231 CreatedIndex uint64 232 // ModifiedIndex for this kv pair 233 ModifiedIndex uint64 234 // Lock is a generic interface to represent a lock held on a key. 235 Lock interface{} 236 } 237 238 // KVPairs list of KVPairs 239 type KVPairs []*KVPair 240 241 // Tx Interface to transactionally apply updates to a set of keys. 242 type Tx interface { 243 // Put specified key value pair in TX. 244 Put(key string, value interface{}, ttl uint64) (*KVPair, error) 245 // Get returns KVPair in this TXs view. If not found, returns value from 246 // backing KVDB. 247 Get(key string) (*KVPair, error) 248 // Get same as get except that value has the unmarshalled value. 249 GetVal(key string, value interface{}) (*KVPair, error) 250 // Prepare returns an error it transaction cannot be logged. 251 Prepare() error 252 // Commit propagates updates to the KVDB. No operations on this Tx are 253 // allowed after commit. 254 Commit() error 255 // Abort aborts this transaction. No operations on this Tx are allowed 256 // afer commit. 257 Abort() error 258 } 259 260 // Kvdb interface implemented by backing datastores. 261 // 262 //go:generate mockgen -package=mock -destination=mock/mock_kvdb.go . Kvdb 263 type Kvdb interface { 264 Controller 265 // String representation of backend datastore. 266 String() string 267 // Capbilities - see KVCapabilityXXX 268 Capabilities() int 269 // Get returns KVPair that maps to specified key or ErrNotFound. 270 Get(key string) (*KVPair, error) 271 // Get returns KVPair that maps to specified key or ErrNotFound. If found 272 // value contains the unmarshalled result or error is ErrUnmarshal 273 GetVal(key string, value interface{}) (*KVPair, error) 274 // GetWithCopy returns a copy of the value as an interface for the specified key 275 GetWithCopy(key string, copySelect CopySelect) (interface{}, error) 276 // Put inserts value at key in kvdb. If value is a runtime.Object, it is 277 // marshalled. If Value is []byte it is set directly. If Value is a string, 278 // its byte representation is stored. 279 Put(key string, value interface{}, ttl uint64) (*KVPair, error) 280 // Create is the same as Put except that ErrExist is returned if the key exists. 281 Create(key string, value interface{}, ttl uint64) (*KVPair, error) 282 // Update is the same as Put except that ErrNotFound is returned if the key 283 // does not exist. 284 Update(key string, value interface{}, ttl uint64) (*KVPair, error) 285 // Enumerate returns a list of KVPair for all keys that share the specified prefix. 286 Enumerate(prefix string) (KVPairs, error) 287 // EnumerateWithSelect returns a copy of all values under the prefix that satisfy the select 288 // function in the provided output array of interfaces 289 EnumerateWithSelect(prefix string, enumerateSelect EnumerateSelect, copySelect CopySelect) ([]interface{}, error) 290 // EnumerateKVPWithSelect returns a copy of all the KVPairs under the prefix that satisfy the select 291 // function in the provided output array of key-value pairs 292 EnumerateKVPWithSelect(prefix string, enumerateSelect EnumerateKVPSelect, copySelect CopyKVPSelect) (KVPairs, error) 293 // Delete deletes the KVPair specified by the key. ErrNotFound is returned 294 // if the key is not found. The old KVPair is returned if successful. 295 Delete(key string) (*KVPair, error) 296 // DeleteTree same as Delete execpt that all keys sharing the prefix are 297 // deleted. 298 DeleteTree(prefix string) error 299 // Keys returns an array of keys that share specified prefix (ie. "1st level directory"). 300 // sep parameter defines a key-separator, and if not provided the "/" is assumed. 301 Keys(prefix, sep string) ([]string, error) 302 // CompareAndSet updates value at kvp.Key if the previous resident 303 // satisfies conditions set in flags and optional prevValue. 304 CompareAndSet(kvp *KVPair, flags KVFlags, prevValue []byte) (*KVPair, error) 305 // CompareAndDelete deletes value at kvp.Key if the previous resident matches 306 // satisfies conditions set in flags. 307 CompareAndDelete(kvp *KVPair, flags KVFlags) (*KVPair, error) 308 // WatchKey calls watchCB everytime a value at key is updated. waitIndex 309 // is the oldest ModifiedIndex of a KVPair for which updates are requestd. 310 WatchKey(key string, waitIndex uint64, opaque interface{}, watchCB WatchCB) error 311 // WatchTree is the same as WatchKey except that watchCB is triggered 312 // for updates on all keys that share the prefix. 313 WatchTree(prefix string, waitIndex uint64, opaque interface{}, watchCB WatchCB) error 314 // Snapshot returns a kvdb snapshot of the provided list of prefixes and the last updated index. 315 // If no prefixes are provided, then the whole kvdb tree is snapshotted and could be potentially an expensive operation 316 // If consistent is true, then snapshot is going to return all the updates happening during the snapshot operation and the last 317 // updated index from the snapshot 318 Snapshot(prefixes []string, consistent bool) (Kvdb, uint64, error) 319 // SnapPut records the key value pair including the index. 320 SnapPut(kvp *KVPair) (*KVPair, error) 321 // LockWithID locks the specified key and associates a lockerID with it, probably to identify 322 // who acquired the lock. The KVPair returned should be used to unlock. 323 LockWithID(key string, lockerID string) (*KVPair, error) 324 // Lock locks the specified key. The KVPair returned should be used to unlock. 325 Lock(key string) (*KVPair, error) 326 // LockWithTimeout locks with specified key and associates a lockerID with it. 327 // lockTryDuration is the maximum time that can be spent trying to acquire 328 // lock, else return error. 329 // lockHoldDuration is the maximum time the lock can be held, after which 330 // FatalCb is invoked. 331 // The KVPair returned should be used to unlock if successful. 332 LockWithTimeout(key string, lockerID string, lockTryDuration time.Duration, 333 lockHoldDuration time.Duration) (*KVPair, error) 334 // IsKeyLocked returns a boolean if the lock is held or not. If held, returns the owner. 335 IsKeyLocked(key string) (bool, string, error) 336 // Unlock kvp previously acquired through a call to lock. 337 Unlock(kvp *KVPair) error 338 // TxNew returns a new Tx coordinator object or ErrNotSupported 339 TxNew() (Tx, error) 340 // AddUser adds a new user to kvdb 341 AddUser(username string, password string) error 342 // RemoveUser removes a user from kvdb 343 RemoveUser(username string) error 344 // GrantUserAccess grants user access to a subtree/prefix based on the permission 345 GrantUserAccess(username string, permType PermissionType, subtree string) error 346 // RevokeUsersAccess revokes user's access to a subtree/prefix based on the permission 347 RevokeUsersAccess(username string, permType PermissionType, subtree string) error 348 // SetFatalCb sets the function to be called in case of fatal errors 349 SetFatalCb(f FatalErrorCB) 350 // SetLockHoldDuration sets maximum time a lock may be held 351 SetLockHoldDuration(timeout time.Duration) 352 // GetLockTryDuration gets the maximum time to attempt to get a lock. 353 GetLockTryDuration() time.Duration 354 // GetLockHoldDuration gets the currently set lock hold timeout 355 GetLockHoldDuration() time.Duration 356 // Serialize serializes all the keys under the domain and returns a byte array 357 Serialize() ([]byte, error) 358 // Deserialize deserializes the given byte array into a list of kv pairs 359 Deserialize([]byte) (KVPairs, error) 360 // Compact removes the history before the specified index/revision to reduce the space and memory usage 361 Compact(index uint64) error 362 KvdbWrapper 363 } 364 365 // ReplayCb provides info required for replay 366 type ReplayCb struct { 367 // Prefix is the watch key/tree prefix 368 Prefix string 369 // WaitIndex is the index after which updates must be returned 370 WaitIndex uint64 371 // Opaque is a hint returned by the caller 372 Opaque interface{} 373 // WatchCB is the watch callback 374 WatchCB WatchCB 375 } 376 377 // UpdatesCollector collects updates from kvdb. 378 type UpdatesCollector interface { 379 // Stop collecting updates 380 Stop() 381 // ReplayUpdates replays the collected updates. 382 // Returns the version until the replay's were done 383 // and any errors it encountered. 384 ReplayUpdates(updateCb []ReplayCb) (uint64, error) 385 } 386 387 // NewUpdatesCollector creates new Kvdb collector that collects updates 388 // starting at startIndex + 1 index. 389 func NewUpdatesCollector( 390 db Kvdb, 391 prefix string, 392 startIndex uint64, 393 ) (UpdatesCollector, error) { 394 collector := &updatesCollectorImpl{updates: make([]*kvdbUpdate, 0), 395 startIndex: startIndex} 396 logrus.Infof("Starting collector watch on %v at %v", prefix, startIndex) 397 if err := db.WatchTree(prefix, startIndex, nil, collector.watchCb); err != nil { 398 return nil, err 399 } 400 return collector, nil 401 } 402 403 // List of kvdb controller ports 404 const ( 405 // PeerPort is the port on which peers identify themselves 406 PeerPort = "2380" 407 // ClientPort is the port on which clients send requests to kvdb. 408 ClientPort = "2379" 409 ) 410 411 // MemberInfo represents a member of the kvdb cluster 412 type MemberInfo struct { 413 // PeerUrls is this member's URL on which it talks to its peers 414 PeerUrls []string 415 // ClientUrls is this member's URL on which clients can reach this member. 416 ClientUrls []string 417 // Leader indicates if this member is the leader of this cluster. 418 Leader bool 419 // DbSize is the current DB size as seen by this member. 420 DbSize int64 421 // IsHealthy indicates the health of the member. 422 IsHealthy bool 423 // ID is the string representation of member's ID 424 ID string 425 // Name of the member. A member which has not started has an empty Name. 426 Name string 427 // HasStarted indicates if this member has successfully started kvdb. 428 HasStarted bool 429 // IsLearner indicates if this member is a learner (i.e. not yet promoted to a full voting member). 430 IsLearner bool 431 } 432 433 // Controller interface provides APIs to manage Kvdb Cluster and Kvdb Clients. 434 type Controller interface { 435 // AddMember adds a new member to an existing kvdb cluster. Add API should be 436 // invoked on an existing kvdb node where kvdb is already running. It should be 437 // followed by a Setup call on the node which is being added. 438 // Returns: map of nodeID to peerUrls of all members in the initial cluster or error. 439 AddMember(nodeIP, nodePeerPort, nodeName string) (map[string][]string, error) 440 441 // AddLearner is same as AddMember except that the new member is added as a learner. 442 // It is caller's responsibility to promote it to a full voting member. 443 AddLearner(nodeIP, nodePeerPort, nodeName string) (map[string][]string, error) 444 445 // RemoveMember removes a member based on its Name from an existing kvdb cluster. 446 // Returns: error if it fails to remove a member 447 RemoveMember(nodeName, nodeIP string) error 448 449 // RemoveMemberByID removes a member based on its ID from an existing kvdb cluster. 450 // Returns: error if it fails to remove a member 451 RemoveMemberByID(memberID uint64) error 452 453 // UpdateMember updates the IP for the given node in an existing kvdb cluster 454 // Returns: map of nodeID to peerUrls of all members from the existing cluster 455 UpdateMember(nodeIP, nodePeerPort, nodeName string) (map[string][]string, error) 456 457 // ListMembers enumerates the members of the kvdb cluster. It includes both the 458 // started and unstarted members. 459 // Returns: the member's ID to MemberInfo mappings for all the members 460 ListMembers() (map[uint64]*MemberInfo, error) 461 462 // SetEndpoints set the kvdb endpoints for the client 463 SetEndpoints(endpoints []string) error 464 465 // GetEndpoints returns the kvdb endpoints for the client 466 GetEndpoints() []string 467 468 // Defragment defrags the underlying database for the given endpoint 469 // with a timeout specified in seconds 470 Defragment(endpoint string, timeout int) error 471 } 472 473 func LogFatalErrorCB(err error, format string, args ...interface{}) { 474 logrus.Errorf("encountered fatal error: %v", err) 475 logrus.Panicf(format, args...) 476 }