github.com/portworx/kvdb@v0.0.0-20241107215734-a185a966f535/etcd/v3/kv_etcd.go (about) 1 package etcdv3 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "math/rand" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/portworx/kvdb" 14 "github.com/portworx/kvdb/common" 15 ec "github.com/portworx/kvdb/etcd/common" 16 "github.com/portworx/kvdb/mem" 17 "github.com/sirupsen/logrus" 18 "go.etcd.io/etcd/api/v3/mvccpb" 19 "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" 20 e "go.etcd.io/etcd/client/v3" 21 "go.etcd.io/etcd/client/v3/concurrency" 22 "golang.org/x/net/context" 23 "google.golang.org/grpc" 24 ) 25 26 const ( 27 // Name is the name of this kvdb implementation. 28 Name = "etcdv3-kv" 29 defaultKvRequestTimeout = 10 * time.Second 30 defaultLeaseRequestTimeout = 2 * time.Second 31 defaultMaintenanceTimeout = 7 * time.Second 32 // defaultDefragTimeout in seconds is the timeout for defrag to complete 33 defaultDefragTimeout = 30 34 // defaultSessionTimeout in seconds is used for etcd watch 35 // to detect connectivity issues 36 defaultSessionTimeout = 50 37 // All the below timeouts are similar to the ones set in etcdctl 38 // and are mainly used for etcd client's load balancing. 39 defaultDialTimeout = 2 * time.Second 40 defaultKeepAliveTime = 2 * time.Second 41 defaultKeepAliveTimeout = 6 * time.Second 42 urlPrefix = "http://" 43 urlSecPrefix = "https://" 44 // timeoutMaxRetry is maximum retries before faulting 45 timeoutMaxRetry = 30 46 ) 47 48 var ( 49 defaultMachines = []string{"http://127.0.0.1:2379", "http://[::1]:2379"} 50 // maintenanceClientLock is a lock over the maintenanceClient 51 maintenanceClientLock sync.Mutex 52 ) 53 54 // watchQ to collect updates without blocking 55 type watchQ struct { 56 // q is the producer consumer q 57 q common.WatchUpdateQueue 58 // opaque is returned with the callbacl 59 opaque interface{} 60 // cb is the watch callback 61 cb kvdb.WatchCB 62 // watchRet returns error on channel to indicate stopping of watch 63 watchRet chan error 64 // done is true if watch has finished and no longer active 65 done bool 66 // doneLock protects done boolean 67 doneLock sync.RWMutex 68 } 69 70 func newWatchQ(o interface{}, cb kvdb.WatchCB, watchRet chan error) *watchQ { 71 q := &watchQ{q: common.NewWatchUpdateQueue(), opaque: o, cb: cb, 72 watchRet: watchRet, done: false} 73 go q.start() 74 return q 75 } 76 77 func (w *watchQ) enqueue(key string, kvp *kvdb.KVPair, err error) bool { 78 w.q.Enqueue(key, kvp, err) 79 w.doneLock.RLock() 80 notDone := !w.done 81 w.doneLock.RUnlock() 82 return notDone 83 } 84 85 func isWatchClosedError(err error) bool { 86 return err == kvdb.ErrWatchRevisionCompacted || err == kvdb.ErrWatchStopped 87 } 88 89 func (w *watchQ) start() { 90 for { 91 key, kvp, err := w.q.Dequeue() 92 err = w.cb(key, w.opaque, kvp, err) 93 if err != nil { 94 w.doneLock.Lock() 95 w.done = true 96 w.doneLock.Unlock() 97 logrus.Infof("Watch cb for key %v returned err: %v", key, err) 98 if !isWatchClosedError(err) { 99 // The caller returned an error. Indicate the caller 100 // that the watch has been stopped 101 _ = w.cb(key, w.opaque, nil, kvdb.ErrWatchStopped) 102 } // else we stopped the watch and the caller has been notified 103 // Indicate that watch is returning. 104 close(w.watchRet) 105 break 106 } 107 } 108 } 109 110 func init() { 111 if err := kvdb.Register(Name, New, ec.Version); err != nil { 112 panic(err.Error()) 113 } 114 } 115 116 type etcdKV struct { 117 common.BaseKvdb 118 kvClient *e.Client 119 authClient e.Auth 120 maintenanceClient *e.Client 121 domain string 122 lockRefreshDuration time.Duration 123 ec.EtcdCommon 124 } 125 126 // New constructs a new kvdb.Kvdb. 127 func New( 128 domain string, 129 machines []string, 130 options map[string]string, 131 fatalErrorCb kvdb.FatalErrorCB, 132 ) (kvdb.Kvdb, error) { 133 if len(machines) == 0 { 134 machines = defaultMachines 135 } 136 137 etcdCommon := ec.NewEtcdCommon(options) 138 tls, username, password, err := etcdCommon.GetAuthInfoFromOptions() 139 if err != nil { 140 return nil, err 141 } 142 143 tlsCfg, err := tls.ClientConfig() 144 if err != nil { 145 return nil, err 146 } 147 148 cfg := e.Config{ 149 Endpoints: machines, 150 Username: username, 151 Password: password, 152 DialTimeout: defaultDialTimeout, 153 TLS: tlsCfg, 154 DialKeepAliveTime: defaultKeepAliveTime, 155 DialKeepAliveTimeout: defaultKeepAliveTimeout, 156 157 // As per the comment in go.etcd.io/etcd/client/v3/config.go, we need to pass "grpc.WithBlock()" 158 // to block until the underlying connection is up. Without this, Dial returns immediately and 159 // connecting the server happens in background. This is a behavior change in 3.4. 160 // We use WithBlock to preserve the old behavior. 161 DialOptions: []grpc.DialOption{grpc.WithBlock()}, 162 163 // The time required for a request to fail - 30 sec 164 //HeaderTimeoutPerRequest: time.Duration(10) * time.Second, 165 } 166 kvClient, err := e.New(cfg) 167 if err != nil { 168 if len(tls.CertFile) > 0 { 169 // With secure etcd cluster, etcd client has a bug 170 // where it fails to connect to the etcd cluster if 171 // first endpoint in the list is down. 172 // Shuffle the list of IPs and try again 173 for i := 0; i < len(cfg.Endpoints); i++ { 174 endpoints := rotateEndpointsByOne(cfg.Endpoints) 175 cfg.Endpoints = endpoints 176 kvClient, err = e.New(cfg) 177 if err == nil { 178 break 179 } 180 } 181 } 182 if err != nil { 183 return nil, err 184 } 185 } 186 // Creating a separate client for maintenance APIs. Currently the maintenance client 187 // is only used for the Status API, to fetch the endpoint status. However if the Status 188 // API errors out for an endpoint, the etcd client code marks the pinned address as not reachable 189 // instead of the actual endpoint for which the Status command failed. This causes the etcd 190 // balancer to go into a retry loop trying to fix its healthy endpoints. 191 // https://github.com/etcd-io/etcd/blob/v3.3.1/client/v3/retry.go#L102 192 // keepalive is not required for maintenance requests 193 mCfg := cfg 194 mCfg.DialKeepAliveTime = 0 195 mCfg.DialKeepAliveTimeout = 0 196 mClient, err := e.New(mCfg) 197 if err != nil { 198 return nil, err 199 } 200 201 if domain != "" && !strings.HasSuffix(domain, "/") { 202 domain = domain + "/" 203 } 204 return &etcdKV{ 205 common.BaseKvdb{FatalCb: fatalErrorCb, LockTryDuration: kvdb.DefaultLockTryDuration}, 206 kvClient, 207 e.NewAuth(kvClient), 208 mClient, 209 domain, 210 ec.DefaultLockRefreshDuration, 211 etcdCommon, 212 }, nil 213 } 214 215 func (et *etcdKV) String() string { 216 return Name 217 } 218 219 func (et *etcdKV) Capabilities() int { 220 return kvdb.KVCapabilityOrderedUpdates 221 } 222 223 func (et *etcdKV) Context() (context.Context, context.CancelFunc) { 224 return context.WithTimeout(context.Background(), defaultKvRequestTimeout) 225 } 226 227 func (et *etcdKV) LeaseContext() (context.Context, context.CancelFunc) { 228 return context.WithTimeout(context.Background(), defaultLeaseRequestTimeout) 229 } 230 231 func (et *etcdKV) MaintenanceContextWithLeader() (context.Context, context.CancelFunc) { 232 return context.WithTimeout(getContextWithLeaderRequirement(), defaultMaintenanceTimeout) 233 } 234 235 func (et *etcdKV) MaintenanceContext() (context.Context, context.CancelFunc) { 236 return context.WithTimeout(context.Background(), defaultMaintenanceTimeout) 237 } 238 239 func (et *etcdKV) Get(key string) (*kvdb.KVPair, error) { 240 var ( 241 err error 242 result *e.GetResponse 243 ) 244 key = et.domain + key 245 for i := 0; i < et.GetRetryCount(); i++ { 246 ctx, cancel := et.Context() 247 result, err = et.kvClient.Get(ctx, key) 248 cancel() 249 if err == nil && result != nil { 250 kvs := et.handleGetResponse(result, false) 251 if len(kvs) == 0 { 252 return nil, kvdb.ErrNotFound 253 } 254 return kvs[0], nil 255 } 256 257 retry, err := isRetryNeeded(err, "get", key, i) 258 if retry { 259 continue 260 } 261 return nil, err 262 } 263 return nil, err 264 } 265 266 func (et *etcdKV) GetVal(key string, val interface{}) (*kvdb.KVPair, error) { 267 kvp, err := et.Get(key) 268 if err != nil { 269 return nil, err 270 } 271 if err := json.Unmarshal(kvp.Value, val); err != nil { 272 return kvp, kvdb.ErrUnmarshal 273 } 274 return kvp, nil 275 } 276 277 func (et *etcdKV) Put( 278 key string, 279 val interface{}, 280 ttl uint64, 281 ) (*kvdb.KVPair, error) { 282 b, err := common.ToBytes(val) 283 if err != nil { 284 return nil, err 285 } 286 return et.setWithRetry(key, string(b), ttl) 287 } 288 289 func (et *etcdKV) Create( 290 key string, 291 val interface{}, 292 ttl uint64, 293 ) (*kvdb.KVPair, error) { 294 pathKey := et.domain + key 295 opts := []e.OpOption{} 296 if ttl > 0 { 297 if ttl < 5 { 298 return nil, kvdb.ErrTTLNotSupported 299 } 300 leaseResult, err := et.getLeaseWithRetries(key, int64(ttl)) 301 if err != nil { 302 return nil, err 303 } 304 opts = append(opts, e.WithLease(leaseResult.ID)) 305 306 } 307 b, _ := common.ToBytes(val) 308 ctx, cancel := et.Context() 309 // Txn 310 // If key exist before 311 // Then do nothing (txnResponse.Succeeded == true) 312 // Else put/create the key (txnResponse.Succeeded == false) 313 txnResponse, txnErr := et.kvClient.Txn(ctx).If( 314 e.Compare(e.CreateRevision(pathKey), ">", 0), 315 ).Then().Else( 316 e.OpPut(pathKey, string(b), opts...), 317 e.OpGet(pathKey), 318 ).Commit() 319 cancel() 320 if txnErr != nil { 321 return nil, txnErr 322 } 323 if txnResponse.Succeeded { 324 // The key did exist before 325 return nil, kvdb.ErrExist 326 } 327 328 rangeResponse := txnResponse.Responses[1].GetResponseRange() 329 kvPair := et.resultToKv(rangeResponse.Kvs[0], "create") 330 kvPair.KVDBIndex = uint64(txnResponse.Header.Revision) 331 return kvPair, nil 332 } 333 334 func (et *etcdKV) Update( 335 key string, 336 val interface{}, 337 ttl uint64, 338 ) (*kvdb.KVPair, error) { 339 pathKey := et.domain + key 340 opts := []e.OpOption{} 341 if ttl > 0 { 342 if ttl < 5 { 343 return nil, kvdb.ErrTTLNotSupported 344 } 345 leaseResult, err := et.getLeaseWithRetries(key, int64(ttl)) 346 if err != nil { 347 return nil, err 348 } 349 opts = append(opts, e.WithLease(leaseResult.ID)) 350 351 } 352 b, _ := common.ToBytes(val) 353 ctx, cancel := et.Context() 354 // Txn 355 // If key exist before 356 // Then update key (txnResponse.Succeeded == true) 357 // Else put/create the key (txnResponse.Succeeded == false) 358 txnResponse, txnErr := et.kvClient.Txn(ctx).If( 359 e.Compare(e.CreateRevision(pathKey), ">", 0), 360 ).Then( 361 e.OpPut(pathKey, string(b), opts...), 362 e.OpGet(pathKey), 363 ).Else().Commit() 364 cancel() 365 if txnErr != nil { 366 return nil, txnErr 367 } 368 if !txnResponse.Succeeded { 369 // The key did not exist before 370 return nil, kvdb.ErrNotFound 371 } 372 373 rangeResponse := txnResponse.Responses[1].GetResponseRange() 374 kvPair := et.resultToKv(rangeResponse.Kvs[0], "update") 375 return kvPair, nil 376 } 377 378 func (et *etcdKV) Enumerate(prefix string) (kvdb.KVPairs, error) { 379 prefix = et.domain + prefix 380 var err error 381 382 for i := 0; i < et.GetRetryCount(); i++ { 383 ctx, cancel := et.Context() 384 result, err := et.kvClient.Get( 385 ctx, 386 prefix, 387 e.WithPrefix(), 388 e.WithSort(e.SortByKey, e.SortAscend), 389 ) 390 cancel() 391 if err == nil && result != nil { 392 kvs := et.handleGetResponse(result, true) 393 return kvs, nil 394 } 395 396 retry, err := isRetryNeeded(err, "enumerate", prefix, i) 397 if retry { 398 continue 399 } 400 return nil, err 401 } 402 return nil, err 403 } 404 405 func (et *etcdKV) Delete(key string) (*kvdb.KVPair, error) { 406 // Delete does not return the prev kv value even after setting 407 // the WithPrevKV OpOption. 408 kvp, err := et.Get(key) 409 if err != nil { 410 return nil, err 411 } 412 key = et.domain + key 413 414 ctx, cancel := et.Context() 415 result, err := et.kvClient.Delete( 416 ctx, 417 key, 418 e.WithPrevKV(), 419 ) 420 cancel() 421 if err == nil { 422 if result.Deleted == 0 { 423 return nil, kvdb.ErrNotFound 424 } else if result.Deleted > 1 { 425 return nil, fmt.Errorf("incorrect number of keys: %v deleted, result: %v", 426 key, result) 427 } 428 kvp.Action = kvdb.KVDelete 429 return kvp, nil 430 } 431 432 if err == rpctypes.ErrGRPCEmptyKey { 433 return nil, kvdb.ErrNotFound 434 } 435 436 return nil, err 437 } 438 439 func (et *etcdKV) DeleteTree(prefix string) error { 440 prefix = et.domain + prefix 441 if !strings.HasSuffix(prefix, kvdb.DefaultSeparator) { 442 prefix += kvdb.DefaultSeparator 443 } 444 445 ctx, cancel := et.Context() 446 _, err := et.kvClient.Delete( 447 ctx, 448 prefix, 449 e.WithPrevKV(), 450 e.WithPrefix(), 451 ) 452 cancel() 453 return err 454 } 455 456 func (et *etcdKV) Keys(prefix, sep string) ([]string, error) { 457 var ( 458 retList = make([]string, 0, 10) 459 seen = make(map[string]bool) 460 err error 461 result *e.GetResponse 462 retry bool 463 ) 464 if sep == "" { 465 sep = "/" 466 } 467 lenPrefix := len(prefix) 468 lenSep := len(sep) 469 if lenPrefix > 0 && prefix[lenPrefix-lenSep:] != sep { 470 prefix += sep 471 lenPrefix += lenSep 472 } 473 for i := 0; i < et.GetRetryCount(); i++ { 474 ctx, cancel := et.Context() 475 result, err = et.kvClient.Get( 476 ctx, 477 et.domain+prefix, 478 e.WithPrefix(), 479 e.WithKeysOnly(), 480 e.WithSort(e.SortByKey, e.SortAscend), 481 ) 482 cancel() 483 if err == nil && result != nil { 484 kvs := et.handleGetResponse(result, false) 485 for _, kv := range kvs { 486 key := kv.Key 487 if lenPrefix > 0 { 488 if strings.HasPrefix(key, prefix) { 489 // strip prefix 490 key = key[lenPrefix:] 491 } 492 } 493 if idx := strings.Index(key, sep); idx > 0 { 494 // extract key's first "directory" 495 key = key[:idx] 496 } 497 if !seen[key] { 498 // add unique "first level" keys/dirs 499 retList = append(retList, key) 500 seen[key] = true 501 } 502 } 503 return retList, nil 504 } 505 506 retry, err = isRetryNeeded(err, "keys", prefix, i) 507 if retry { 508 continue 509 } 510 return retList, err 511 } 512 return retList, nil 513 } 514 515 func (et *etcdKV) CompareAndSet( 516 kvp *kvdb.KVPair, 517 flags kvdb.KVFlags, 518 prevValue []byte, 519 ) (*kvdb.KVPair, error) { 520 fn := "cas" 521 var ( 522 leaseResult *e.LeaseGrantResponse 523 txnResponse *e.TxnResponse 524 txnErr, err error 525 ) 526 key := et.domain + kvp.Key 527 cmp := e.Compare(e.Value(key), "=", string(prevValue)) 528 if (flags & kvdb.KVModifiedIndex) != 0 { 529 cmp = e.Compare(e.ModRevision(key), "=", int64(kvp.ModifiedIndex)) 530 } 531 532 for i := 0; i < timeoutMaxRetry; i++ { 533 opts := []e.OpOption{} 534 if (flags & kvdb.KVTTL) != 0 { 535 leaseResult, err = et.getLeaseWithRetries(key, kvp.TTL) 536 if err != nil { 537 return nil, err 538 } 539 opts = append(opts, e.WithLease(leaseResult.ID)) 540 } 541 ctx, cancel := et.Context() 542 txnResponse, txnErr = et.kvClient.Txn(ctx). 543 If(cmp). 544 Then(e.OpPut(key, string(kvp.Value), opts...)). 545 Commit() 546 cancel() 547 if txnErr != nil { 548 // Check if we need to retry 549 retry, txnErr := isRetryNeeded(txnErr, fn, key, i) 550 if !retry { 551 // For all other errors return immediately 552 return nil, txnErr 553 } // retry is needed 554 555 // server timeout 556 kvPair, err := et.Get(kvp.Key) 557 if err != nil { 558 logrus.Errorf("%v: get after retry failed with error: %v", fn, err) 559 return nil, txnErr 560 } 561 if kvPair.ModifiedIndex == kvp.ModifiedIndex { 562 // update did not succeed, retry 563 if i == (timeoutMaxRetry - 1) { 564 et.FatalCb(kvdb.ErrNoConnection, "Too many server retries for CAS: %v", *kvp) 565 return nil, txnErr 566 } 567 continue 568 } else if bytes.Equal(kvp.Value, kvPair.Value) { 569 return kvPair, nil 570 } 571 // else someone else updated the value, return error 572 return nil, txnErr 573 } 574 if !txnResponse.Succeeded { 575 if len(txnResponse.Responses) == 0 { 576 logrus.Infof("Etcd did not return any transaction responses "+ 577 "for key (%v) index (%v)", kvp.Key, kvp.ModifiedIndex) 578 } else { 579 for i, responseOp := range txnResponse.Responses { 580 logrus.Infof("Etcd transaction Response: %v %v", i, 581 responseOp.String()) 582 } 583 } 584 if (flags & kvdb.KVModifiedIndex) != 0 { 585 return nil, kvdb.ErrModified 586 } 587 588 return nil, kvdb.ErrValueMismatch 589 } 590 break 591 } 592 593 kvPair, err := et.Get(kvp.Key) 594 if err != nil { 595 return nil, err 596 } 597 return kvPair, nil 598 } 599 600 func (et *etcdKV) CompareAndDelete( 601 kvp *kvdb.KVPair, 602 flags kvdb.KVFlags, 603 ) (*kvdb.KVPair, error) { 604 fn := "cad" 605 key := et.domain + kvp.Key 606 607 cmp := e.Compare(e.Value(key), "=", string(kvp.Value)) 608 if (flags & kvdb.KVModifiedIndex) != 0 { 609 cmp = e.Compare(e.ModRevision(key), "=", int64(kvp.ModifiedIndex)) 610 } 611 for i := 0; i < timeoutMaxRetry; i++ { 612 ctx, cancel := et.Context() 613 txnResponse, txnErr := et.kvClient.Txn(ctx). 614 If(cmp). 615 Then(e.OpDelete(key)). 616 Commit() 617 cancel() 618 if txnErr != nil { 619 // Check if we need to retry 620 retry, txnErr := isRetryNeeded(txnErr, fn, key, i) 621 if txnErr == kvdb.ErrNotFound { 622 return kvp, nil 623 } else if !retry { 624 // For all other errors return immediately 625 return nil, txnErr 626 } // retry is needed 627 628 // server timeout 629 _, err := et.Get(kvp.Key) 630 if err == kvdb.ErrNotFound { 631 // Our command succeeded 632 return kvp, nil 633 } else if err != nil { 634 logrus.Errorf("%v: get after retry failed with error: %v", fn, err) 635 return nil, txnErr 636 } 637 if i == (timeoutMaxRetry - 1) { 638 et.FatalCb(kvdb.ErrNoConnection, "Too many server retries for CAD: %v", *kvp) 639 return nil, txnErr 640 } 641 continue 642 } 643 if !txnResponse.Succeeded { 644 if len(txnResponse.Responses) == 0 { 645 logrus.Infof("Etcd did not return any transaction responses for key (%v)", kvp.Key) 646 } else { 647 for i, responseOp := range txnResponse.Responses { 648 logrus.Infof("Etcd transaction Response: %v %v", i, responseOp.String()) 649 } 650 } 651 if (flags & kvdb.KVModifiedIndex) != 0 { 652 return nil, kvdb.ErrModified 653 } 654 655 return nil, kvdb.ErrValueMismatch 656 } 657 break 658 } 659 return kvp, nil 660 } 661 662 func (et *etcdKV) WatchKey( 663 key string, 664 waitIndex uint64, 665 opaque interface{}, 666 cb kvdb.WatchCB, 667 ) error { 668 key = et.domain + key 669 go et.watchStart(key, false, waitIndex, opaque, cb) 670 return nil 671 } 672 673 func (et *etcdKV) WatchTree( 674 prefix string, 675 waitIndex uint64, 676 opaque interface{}, 677 cb kvdb.WatchCB, 678 ) error { 679 prefix = et.domain + prefix 680 go et.watchStart(prefix, true, waitIndex, opaque, cb) 681 return nil 682 } 683 684 func (et *etcdKV) isErrCompacted(err error) bool { 685 // err.Error(): 686 // "etcdserver: mvcc: required revision has been compacted" 687 // ErrGRPCCompacted.Error(): 688 // "rpc error: code = OutOfRange desc = etcdserver: mvcc: required revision has been compacted" 689 return strings.Contains(rpctypes.ErrGRPCCompacted.Error(), err.Error()) 690 } 691 692 func (et *etcdKV) Compact( 693 index uint64, 694 ) error { 695 ctx, cancel := et.Context() 696 _, err := et.kvClient.Compact(ctx, int64(index)) 697 cancel() 698 if err != nil && !et.isErrCompacted(err) { 699 return err 700 } 701 return nil 702 } 703 704 func (et *etcdKV) Lock(key string) (*kvdb.KVPair, error) { 705 return et.LockWithID(key, "locked") 706 } 707 708 func (et *etcdKV) LockWithID(key string, lockerID string) ( 709 *kvdb.KVPair, 710 error, 711 ) { 712 return et.LockWithTimeout(key, lockerID, et.LockTryDuration, et.GetLockHoldDuration()) 713 } 714 715 func (et *etcdKV) LockWithTimeout( 716 key string, 717 lockerID string, 718 lockTryDuration time.Duration, 719 lockHoldDuration time.Duration, 720 ) (*kvdb.KVPair, error) { 721 key = et.domain + key 722 duration := time.Second 723 ttl := uint64(ec.DefaultLockTTL) 724 lockTag := ec.LockerIDInfo{LockerID: lockerID} 725 kvPair, err := et.Create(key, lockTag, ttl) 726 startTime := time.Now() 727 for count := 0; err != nil; count++ { 728 time.Sleep(duration) 729 kvPair, err = et.Create(key, lockTag, ttl) 730 if count > 0 && count%15 == 0 && err != nil { 731 currLockerTag := ec.LockerIDInfo{LockerID: ""} 732 if _, errGet := et.GetVal(key, &currLockerTag); errGet == nil { 733 logrus.Warnf("Lock %v locked for %v seconds, tag: %v, err: %v", 734 key, count, currLockerTag, err) 735 } 736 } 737 if err != nil && time.Since(startTime) > lockTryDuration { 738 currLockerTag := ec.LockerIDInfo{LockerID: ""} 739 if _, errGet := et.GetVal(key, &currLockerTag); errGet == nil { 740 return nil, fmt.Errorf("failed to take a lock on %v: lock taken by: %v ", key, currLockerTag.LockerID) 741 } 742 return nil, fmt.Errorf("failed to take a lock on %v", key) 743 } 744 } 745 if err != nil { 746 return nil, err 747 } 748 kvPair.TTL = int64(ttl) 749 kvPair.Lock = &ec.EtcdLock{Done: make(chan struct{}), AcquisitionTime: time.Now()} 750 go et.refreshLock(kvPair, lockerID, lockHoldDuration) 751 return kvPair, err 752 } 753 754 func (et *etcdKV) IsKeyLocked(key string) (bool, string, error) { 755 key = et.domain + key 756 kvPair, err := et.Get(key) 757 if err == kvdb.ErrNotFound { 758 return false, "", nil 759 } else if err != nil { 760 return false, "", err 761 } 762 var lockIdInfo ec.LockerIDInfo 763 err = json.Unmarshal(kvPair.Value, &lockIdInfo) 764 if err != nil { 765 return false, "", kvdb.ErrInvalidLock 766 } 767 return true, lockIdInfo.LockerID, nil 768 } 769 770 func (et *etcdKV) Unlock(kvp *kvdb.KVPair) error { 771 l, ok := kvp.Lock.(*ec.EtcdLock) 772 if !ok { 773 return fmt.Errorf("invalid lock structure for key %v", string(kvp.Key)) 774 } 775 l.Lock() 776 defer l.Unlock() 777 if !l.Unlocked { 778 // Don't modify kvp here, CompareAndDelete does that. 779 if _, err := et.CompareAndDelete(kvp, kvdb.KVFlags(0)); err != nil { 780 // ignore error since the lock will expire automatically after we stop the refresh 781 logrus.Warnf("Ignoring error when unlocking key %s: %v", kvp.Key, err) 782 } 783 // Close the channel without writing to it. This avoids blocking indefinitely when writing to the channel 784 // after refreshLock has exited (e.g. after encountering an error). 785 close(l.Done) 786 l.Unlocked = true 787 } 788 return nil 789 } 790 791 func (et *etcdKV) TxNew() (kvdb.Tx, error) { 792 return nil, kvdb.ErrNotSupported 793 } 794 795 func (et *etcdKV) getAction(action string) kvdb.KVAction { 796 switch action { 797 798 case "create": 799 return kvdb.KVCreate 800 case "set", "update", "compareAndSwap": 801 return kvdb.KVSet 802 case "delete", "compareAndDelete": 803 return kvdb.KVDelete 804 case "get": 805 return kvdb.KVGet 806 default: 807 return kvdb.KVUknown 808 } 809 } 810 811 func (et *etcdKV) resultToKv(resultKv *mvccpb.KeyValue, action string) *kvdb.KVPair { 812 kvp := &kvdb.KVPair{ 813 Value: resultKv.Value, 814 ModifiedIndex: uint64(resultKv.ModRevision), 815 CreatedIndex: uint64(resultKv.ModRevision), 816 } 817 818 kvp.Action = et.getAction(action) 819 key := string(resultKv.Key[:]) 820 kvp.Key = strings.TrimPrefix(key, et.domain) 821 return kvp 822 } 823 824 func isHidden(key string) bool { 825 tokens := strings.Split(key, "/") 826 keySuffix := tokens[len(tokens)-1] 827 return keySuffix != "" && keySuffix[0] == '_' 828 } 829 830 func (et *etcdKV) handleGetResponse(result *e.GetResponse, removeHidden bool) kvdb.KVPairs { 831 kvs := []*kvdb.KVPair{} 832 for i := range result.Kvs { 833 if removeHidden && isHidden(string(result.Kvs[i].Key[:])) { 834 continue 835 } 836 kvs = append(kvs, et.resultToKv(result.Kvs[i], "get")) 837 } 838 return kvs 839 } 840 841 func (et *etcdKV) handlePutResponse(result *e.PutResponse, key string) (*kvdb.KVPair, error) { 842 kvPair, err := et.Get(key) 843 if err != nil { 844 return nil, err 845 } 846 kvPair.Action = kvdb.KVSet 847 return kvPair, nil 848 } 849 850 func (et *etcdKV) setWithRetry(key, value string, ttl uint64) (*kvdb.KVPair, error) { 851 var ( 852 err error 853 i int 854 result *e.PutResponse 855 ) 856 pathKey := et.domain + key 857 if ttl > 0 && ttl < 5 { 858 return nil, kvdb.ErrTTLNotSupported 859 } 860 for i = 0; i < et.GetRetryCount(); i++ { 861 if ttl > 0 { 862 var leaseResult *e.LeaseGrantResponse 863 leaseCtx, leaseCancel := et.Context() 864 leaseResult, err = et.kvClient.Grant(leaseCtx, int64(ttl)) 865 leaseCancel() 866 if err != nil { 867 goto handle_error 868 } 869 ctx, cancel := et.Context() 870 result, err = et.kvClient.Put(ctx, pathKey, value, e.WithLease(leaseResult.ID)) 871 cancel() 872 if err == nil && result != nil { 873 kvp, err := et.handlePutResponse(result, key) 874 if err != nil { 875 return nil, err 876 } 877 kvp.TTL = int64(ttl) 878 return kvp, nil 879 } 880 goto handle_error 881 } else { 882 ctx, cancel := et.Context() 883 result, err = et.kvClient.Put(ctx, pathKey, value) 884 cancel() 885 if err == nil && result != nil { 886 kvp, err := et.handlePutResponse(result, key) 887 if err != nil { 888 return nil, err 889 } 890 kvp.TTL = 0 891 return kvp, nil 892 893 } 894 goto handle_error 895 } 896 handle_error: 897 var retry bool 898 retry, err = isRetryNeeded(err, "set", key, i) 899 if retry { 900 continue 901 } 902 goto out 903 } 904 905 out: 906 outErr := err 907 // It's possible that update succeeded but the re-update failed. 908 // Check only if the original error was a cluster error. 909 if i > 0 && i < et.GetRetryCount() && err != nil { 910 kvp, err := et.Get(key) 911 if err == nil && bytes.Equal(kvp.Value, []byte(value)) { 912 return kvp, nil 913 } 914 } 915 916 return nil, outErr 917 } 918 919 func (et *etcdKV) refreshLock( 920 kvPair *kvdb.KVPair, 921 tag string, 922 lockHoldDuration time.Duration, 923 ) { 924 l := kvPair.Lock.(*ec.EtcdLock) 925 ttl := kvPair.TTL 926 refresh := time.NewTicker(et.lockRefreshDuration) 927 var ( 928 keyString string 929 currentRefresh time.Time 930 prevRefresh time.Time 931 startTime time.Time 932 ) 933 if kvPair != nil { 934 keyString = kvPair.Key 935 } 936 startTime = time.Now() 937 lockMsgString := keyString + ",tag=" + tag 938 defer refresh.Stop() 939 for { 940 select { 941 case <-refresh.C: 942 l.Lock() 943 if !l.Unlocked { 944 et.CheckLockTimeout(lockMsgString, startTime, lockHoldDuration) 945 kvPair.TTL = ttl 946 kvp, err := et.CompareAndSet( 947 kvPair, 948 kvdb.KVTTL|kvdb.KVModifiedIndex, 949 kvPair.Value, 950 ) 951 currentRefresh = time.Now() 952 if err != nil { 953 et.FatalCb(kvdb.ErrLockRefreshFailed, 954 "Error refreshing lock. [Tag %v] [Err: %v] [Acquisition Time: %v]"+ 955 " [Current Refresh: %v] [Previous Refresh: %v] [Modified Index: %v]", 956 lockMsgString, err, l.AcquisitionTime, currentRefresh, prevRefresh, kvPair.ModifiedIndex, 957 ) 958 l.Err = err 959 l.Unlock() 960 return 961 } 962 prevRefresh = currentRefresh 963 kvPair.ModifiedIndex = kvp.ModifiedIndex 964 } 965 l.Unlock() 966 case <-l.Done: 967 return 968 } 969 } 970 } 971 972 func (et *etcdKV) watchStart( 973 key string, 974 recursive bool, 975 waitIndex uint64, 976 opaque interface{}, 977 cb kvdb.WatchCB, 978 ) { 979 opts := []e.OpOption{} 980 opts = append(opts, e.WithCreatedNotify()) 981 if recursive { 982 opts = append(opts, e.WithPrefix()) 983 } 984 if waitIndex != 0 { 985 opts = append(opts, e.WithRev(int64(waitIndex+1))) 986 } 987 sessionChan := make(chan int, 1) 988 var ( 989 session *concurrency.Session 990 err error 991 watchStopLock sync.Mutex 992 watchStopped bool 993 ) 994 go func() { 995 session, err = concurrency.NewSession( 996 et.kvClient, 997 concurrency.WithTTL(defaultSessionTimeout)) 998 close(sessionChan) 999 }() 1000 1001 select { 1002 case <-sessionChan: 1003 if err != nil { 1004 logrus.Errorf("Failed to establish session for etcd client watch: %v", err) 1005 _ = cb(key, opaque, nil, kvdb.ErrWatchStopped) 1006 return 1007 } 1008 case <-time.After(defaultKvRequestTimeout): 1009 logrus.Errorf("Failed to establish session for etcd client watch." + 1010 " Timeout!. Etcd cluster not reachable") 1011 _ = cb(key, opaque, nil, kvdb.ErrWatchStopped) 1012 return 1013 } 1014 ctx, watchCancel := context.WithCancel(getContextWithLeaderRequirement()) 1015 watchRet := make(chan error) 1016 watchChan := et.kvClient.Watch(ctx, key, opts...) 1017 watchQ := newWatchQ(opaque, cb, watchRet) 1018 go func() { 1019 for wresp := range watchChan { 1020 if wresp.Created { 1021 continue 1022 } 1023 if wresp.Canceled { 1024 logrus.Errorf("Watch on key %v cancelled. Error: %v", key, 1025 wresp.Err()) 1026 retError := kvdb.ErrWatchStopped 1027 if et.isErrCompacted(wresp.Err()) { 1028 retError = kvdb.ErrWatchRevisionCompacted 1029 } 1030 watchQ.enqueue(key, nil, retError) 1031 return 1032 } else { 1033 for _, ev := range wresp.Events { 1034 var action string 1035 if ev.Type == mvccpb.PUT { 1036 if ev.Kv.Version == 1 { 1037 action = "create" 1038 } else { 1039 action = "set" 1040 } 1041 } else if ev.Type == mvccpb.DELETE { 1042 action = "delete" 1043 } else { 1044 action = "unknown" 1045 } 1046 if !watchQ.enqueue(key, et.resultToKv(ev.Kv, action), err) { 1047 return 1048 } 1049 } 1050 } 1051 } 1052 logrus.Errorf("Watch on key %v closed without a Cancel response.", key) 1053 watchStopLock.Lock() 1054 // Stop the watch only if it has not been stopped already 1055 if !watchStopped { 1056 watchQ.enqueue(key, nil, kvdb.ErrWatchStopped) 1057 watchStopped = true 1058 } 1059 watchStopLock.Unlock() 1060 }() 1061 1062 select { 1063 case <-session.Done(): // closed by etcd 1064 // Indicate the caller that watch has been canceled 1065 logrus.Errorf("Watch closing session for key: %v", key) 1066 watchStopLock.Lock() 1067 // Stop the watch only if it has not been stopped already 1068 if !watchStopped { 1069 watchQ.enqueue(key, nil, kvdb.ErrWatchStopped) 1070 watchStopped = true 1071 } 1072 watchStopLock.Unlock() 1073 watchCancel() 1074 case <-watchRet: // error in watcher 1075 // Close the context 1076 watchCancel() 1077 session.Close() 1078 logrus.Errorf("Watch for %v stopped", key) 1079 return 1080 } 1081 } 1082 1083 func (et *etcdKV) Snapshot(prefixes []string, consistent bool) (kvdb.Kvdb, uint64, error) { 1084 if len(prefixes) == 0 { 1085 prefixes = []string{""} 1086 } else { 1087 prefixes = append(prefixes, ec.Bootstrap) 1088 prefixes = common.PrunePrefixes(prefixes) 1089 } 1090 // Create a new bootstrap key 1091 watchClosed := false 1092 var ( 1093 lowestKvdbIndex, highestKvdbIndex uint64 1094 bootStrapKeyLow, bootStrapKeyHigh string 1095 r int64 1096 updates []*kvdb.KVPair 1097 ) 1098 done := make(chan error) 1099 mutex := &sync.Mutex{} 1100 1101 // watch callback function 1102 cb := func( 1103 prefix string, 1104 opaque interface{}, 1105 kvp *kvdb.KVPair, 1106 err error, 1107 ) error { 1108 var watchErr error 1109 var sendErr error 1110 var m *sync.Mutex 1111 ok := false 1112 1113 if err != nil { 1114 if err == kvdb.ErrWatchStopped && watchClosed { 1115 return nil 1116 } 1117 logrus.Errorf("Watch returned error: %v", err) 1118 watchErr = err 1119 sendErr = err 1120 goto errordone 1121 } 1122 1123 if kvp == nil { 1124 logrus.Infof("Snapshot error, nil kvp") 1125 watchErr = fmt.Errorf("kvp is nil") 1126 sendErr = watchErr 1127 goto errordone 1128 } 1129 1130 m, ok = opaque.(*sync.Mutex) 1131 if !ok { 1132 logrus.Infof("Snapshot error, failed to get mutex") 1133 watchErr = fmt.Errorf("failed to get mutex") 1134 sendErr = watchErr 1135 goto errordone 1136 } 1137 1138 m.Lock() 1139 defer m.Unlock() 1140 for _, configuredPrefix := range prefixes { 1141 if strings.HasPrefix(kvp.Key, configuredPrefix) { 1142 updates = append(updates, kvp) 1143 if highestKvdbIndex > 0 && kvp.ModifiedIndex >= highestKvdbIndex { 1144 // Done applying changes. 1145 watchClosed = true 1146 watchErr = fmt.Errorf("done") 1147 sendErr = nil 1148 goto errordone 1149 } 1150 break 1151 } 1152 } 1153 1154 return nil 1155 errordone: 1156 done <- sendErr 1157 return watchErr 1158 } 1159 1160 if consistent { 1161 // For a consistent snapshot, start a watch to track updates 1162 // happening until we enumerate all the keys 1163 if err := et.WatchTree("", 0, mutex, cb); err != nil { 1164 return nil, 0, fmt.Errorf("failed to start watch: %v", err) 1165 } 1166 r = rand.New(rand.NewSource(time.Now().UnixNano())).Int63() 1167 bootStrapKeyLow = ec.Bootstrap + strconv.FormatInt(r, 10) + 1168 strconv.FormatInt(time.Now().UnixNano(), 10) 1169 kvPair, err := et.Put(bootStrapKeyLow, time.Now().UnixNano(), 0) 1170 if err != nil { 1171 return nil, 0, fmt.Errorf("failed to create snap bootstrap key %v, "+ 1172 "err: %v", bootStrapKeyLow, err) 1173 } 1174 lowestKvdbIndex = kvPair.ModifiedIndex 1175 } 1176 1177 snapDb, err := mem.New( 1178 et.domain, 1179 nil, 1180 map[string]string{mem.KvSnap: "true"}, 1181 et.FatalCb, 1182 ) 1183 if err != nil { 1184 return nil, 0, fmt.Errorf("failed to create in-mem kv store: %v", err) 1185 } 1186 1187 // enumerate prefix function 1188 enumeratePrefix := func(snapDb kvdb.Kvdb, prefix string) error { 1189 kvPairs, err := et.Enumerate(prefix) 1190 if err != nil { 1191 return fmt.Errorf("failed to enumerate %v: err: %v", prefix, 1192 err) 1193 } 1194 1195 for i := 0; i < len(kvPairs); i++ { 1196 kvPair := kvPairs[i] 1197 if len(kvPair.Value) > 0 { 1198 // Only create a leaf node 1199 _, err := snapDb.SnapPut(kvPair) 1200 if err != nil { 1201 return fmt.Errorf("failed creating snap: %v", err) 1202 } 1203 } else { 1204 newKvPairs, err := et.Enumerate(kvPair.Key) 1205 if err != nil { 1206 return fmt.Errorf("failed to get child keys: %v", err) 1207 } 1208 if len(newKvPairs) == 0 { 1209 // empty value for this key 1210 _, err := snapDb.SnapPut(kvPair) 1211 if err != nil { 1212 return fmt.Errorf("failed creating snap: %v", err) 1213 } 1214 } else if len(newKvPairs) == 1 { 1215 // empty value for this key 1216 _, err := snapDb.SnapPut(newKvPairs[0]) 1217 if err != nil { 1218 return fmt.Errorf("failed creating snap: %v", err) 1219 } 1220 } else { 1221 kvPairs = append(kvPairs, newKvPairs...) 1222 } 1223 } 1224 } 1225 return nil 1226 } 1227 1228 // Enumerate all configured prefixes 1229 for _, prefix := range prefixes { 1230 if err := enumeratePrefix(snapDb, prefix); err != nil { 1231 return nil, 0, err 1232 } 1233 } 1234 1235 if !consistent { 1236 // A consistent snapshot is not required 1237 // return all the enumerated keys 1238 return snapDb, 0, nil 1239 } 1240 1241 // take the lock before we Put a key so that 1242 // the highestKvdbIndex will be set before the watch callback is invoked 1243 mutex.Lock() 1244 // Create bootrap key : highest index 1245 1246 bootStrapKeyHigh = ec.Bootstrap + strconv.FormatInt(r, 10) + 1247 strconv.FormatInt(time.Now().UnixNano(), 10) 1248 kvPair, err := et.Put(bootStrapKeyHigh, time.Now().UnixNano(), 0) 1249 if err != nil { 1250 return nil, 0, fmt.Errorf("failed to create snap bootstrap key %v, "+ 1251 "err: %v", bootStrapKeyHigh, err) 1252 } 1253 1254 highestKvdbIndex = kvPair.ModifiedIndex 1255 mutex.Unlock() 1256 1257 // wait until watch finishes 1258 err = <-done 1259 if err != nil { 1260 return nil, 0, err 1261 } 1262 1263 // apply all updates between lowest and highest kvdb index 1264 for _, kvPair := range updates { 1265 if kvPair.ModifiedIndex < highestKvdbIndex && 1266 kvPair.ModifiedIndex > lowestKvdbIndex { 1267 if kvPair.Action == kvdb.KVDelete { 1268 _, err = snapDb.Delete(kvPair.Key) 1269 // A Delete key was issued between our first lowestKvdbIndex Put 1270 // and Enumerate APIs in this function 1271 if err == kvdb.ErrNotFound { 1272 err = nil 1273 } 1274 } else { 1275 _, err = snapDb.SnapPut(kvPair) 1276 } 1277 if err != nil { 1278 return nil, 0, fmt.Errorf("failed to apply update to snap: %v", err) 1279 } 1280 1281 } 1282 } 1283 1284 _, err = et.Delete(bootStrapKeyLow) 1285 if err != nil { 1286 return nil, 0, fmt.Errorf("failed to delete snap bootstrap key: %v, "+ 1287 "err: %v", bootStrapKeyLow, err) 1288 } 1289 _, err = et.Delete(bootStrapKeyHigh) 1290 if err != nil { 1291 return nil, 0, fmt.Errorf("failed to delete snap bootstrap key: %v, "+ 1292 "err: %v", bootStrapKeyHigh, err) 1293 } 1294 1295 return snapDb, highestKvdbIndex, nil 1296 } 1297 1298 func (et *etcdKV) EnumerateWithSelect( 1299 prefix string, 1300 enumerateSelect kvdb.EnumerateSelect, 1301 copySelect kvdb.CopySelect, 1302 ) ([]interface{}, error) { 1303 return nil, kvdb.ErrNotSupported 1304 } 1305 1306 func (et *etcdKV) EnumerateKVPWithSelect( 1307 prefix string, 1308 enumerateSelect kvdb.EnumerateKVPSelect, 1309 copySelect kvdb.CopyKVPSelect, 1310 ) (kvdb.KVPairs, error) { 1311 return nil, kvdb.ErrNotSupported 1312 } 1313 1314 func (et *etcdKV) GetWithCopy( 1315 key string, 1316 copySelect kvdb.CopySelect, 1317 ) (interface{}, error) { 1318 return nil, kvdb.ErrNotSupported 1319 } 1320 1321 func (et *etcdKV) SnapPut(snapKvp *kvdb.KVPair) (*kvdb.KVPair, error) { 1322 return nil, kvdb.ErrNotSupported 1323 } 1324 1325 func (et *etcdKV) AddUser(username string, password string) error { 1326 // Create a role for this user 1327 roleName := username 1328 _, err := et.authClient.RoleAdd(context.Background(), roleName) 1329 if err != nil { 1330 return err 1331 } 1332 // Create the user 1333 _, err = et.authClient.UserAdd(context.Background(), username, password) 1334 if err != nil { 1335 return err 1336 } 1337 // Assign role to user 1338 _, err = et.authClient.UserGrantRole(context.Background(), username, roleName) 1339 return err 1340 } 1341 1342 func (et *etcdKV) RemoveUser(username string) error { 1343 // Revoke user from this role 1344 roleName := username 1345 _, err := et.authClient.UserRevokeRole(context.Background(), username, roleName) 1346 if err != nil { 1347 return err 1348 } 1349 // Remove the role defined for this user 1350 _, err = et.authClient.RoleDelete(context.Background(), roleName) 1351 if err != nil { 1352 return err 1353 } 1354 // Remove the user 1355 _, err = et.authClient.UserDelete(context.Background(), username) 1356 return err 1357 } 1358 1359 func (et *etcdKV) GrantUserAccess(username string, permType kvdb.PermissionType, subtree string) error { 1360 var domain string 1361 if et.domain[0] == '/' { 1362 domain = et.domain 1363 } else { 1364 domain = "/" + et.domain 1365 } 1366 subtree = domain + subtree 1367 etcdPermType, err := getEtcdPermType(permType) 1368 if err != nil { 1369 return err 1370 } 1371 // A role for this user has already been created 1372 // Just assign the subtree to this role 1373 roleName := username 1374 _, err = et.authClient.RoleGrantPermission(context.Background(), roleName, subtree, "", e.PermissionType(etcdPermType)) 1375 return err 1376 } 1377 1378 func (et *etcdKV) RevokeUsersAccess(username string, permType kvdb.PermissionType, subtree string) error { 1379 var domain string 1380 if et.domain[0] == '/' { 1381 domain = et.domain 1382 } else { 1383 domain = "/" + et.domain 1384 } 1385 subtree = domain + subtree 1386 roleName := username 1387 // A role for this user should ideally exist 1388 // Revoke the specfied permission for that subtree 1389 _, err := et.authClient.RoleRevokePermission(context.Background(), roleName, subtree, "") 1390 return err 1391 } 1392 1393 func (et *etcdKV) AddMember( 1394 nodeIP string, 1395 nodePeerPort string, 1396 nodeName string, 1397 ) (map[string][]string, error) { 1398 return et.addMember(nodeIP, nodePeerPort, nodeName, false) 1399 } 1400 1401 func (et *etcdKV) AddLearner( 1402 nodeIP string, 1403 nodePeerPort string, 1404 nodeName string, 1405 ) (map[string][]string, error) { 1406 return et.addMember(nodeIP, nodePeerPort, nodeName, true) 1407 } 1408 1409 func (et *etcdKV) addMember( 1410 nodeIP string, 1411 nodePeerPort string, 1412 nodeName string, 1413 isLearner bool, 1414 ) (map[string][]string, error) { 1415 var err error 1416 peerURLs := et.listenPeerUrls(nodeIP, nodePeerPort) 1417 ctx, cancel := et.MaintenanceContextWithLeader() 1418 if isLearner { 1419 _, err = et.kvClient.MemberAddAsLearner(ctx, peerURLs) 1420 } else { 1421 _, err = et.kvClient.MemberAdd(ctx, peerURLs) 1422 } 1423 cancel() 1424 if err != nil { 1425 return nil, err 1426 } 1427 resp := make(map[string][]string) 1428 ctx, cancel = et.MaintenanceContextWithLeader() 1429 memberListResponse, err := et.kvClient.MemberList(ctx) 1430 cancel() 1431 if err != nil { 1432 return nil, err 1433 } 1434 for _, member := range memberListResponse.Members { 1435 if member.Name == "" { 1436 // Newly added member 1437 resp[nodeName] = member.PeerURLs 1438 } else { 1439 resp[member.Name] = member.PeerURLs 1440 } 1441 } 1442 if _, ok := resp[nodeName]; !ok { 1443 logrus.Warnf("%s not found in kvdb memberlist. Adding it explicitly.", nodeName) 1444 resp[nodeName] = peerURLs 1445 } 1446 return resp, nil 1447 } 1448 1449 func (et *etcdKV) UpdateMember( 1450 nodeIP string, 1451 nodePeerPort string, 1452 nodeName string, 1453 ) (map[string][]string, error) { 1454 peerURLs := et.listenPeerUrls(nodeIP, nodePeerPort) 1455 ctx, cancel := et.MaintenanceContextWithLeader() 1456 1457 memberListResponse, err := et.kvClient.MemberList(ctx) 1458 cancel() 1459 if err != nil { 1460 return nil, err 1461 } 1462 1463 var updateMemberId uint64 1464 resp := make(map[string][]string) 1465 1466 for _, member := range memberListResponse.Members { 1467 if member.Name == nodeName { 1468 updateMemberId = member.ID 1469 resp[member.Name] = peerURLs 1470 } else { 1471 resp[member.Name] = member.PeerURLs 1472 } 1473 } 1474 if updateMemberId == 0 { 1475 return nil, kvdb.ErrMemberDoesNotExist 1476 } 1477 ctx, cancel = et.MaintenanceContextWithLeader() 1478 _, err = et.kvClient.MemberUpdate(ctx, updateMemberId, peerURLs) 1479 cancel() 1480 if err != nil { 1481 return nil, err 1482 } 1483 return resp, nil 1484 } 1485 1486 func (et *etcdKV) RemoveMember( 1487 nodeName string, 1488 nodeIP string, 1489 ) error { 1490 ctx, cancel := et.MaintenanceContextWithLeader() 1491 memberListResponse, err := et.kvClient.MemberList(ctx) 1492 cancel() 1493 if err != nil { 1494 return err 1495 } 1496 var ( 1497 newClientUrls []string 1498 removeMemberID uint64 1499 ) 1500 1501 for _, member := range memberListResponse.Members { 1502 if member.Name == "" { 1503 // In case of a failed start of an etcd member, the Name field will be empty 1504 // We then try to match the IPs. 1505 if strings.Contains(member.PeerURLs[0], nodeIP) { 1506 removeMemberID = member.ID 1507 } 1508 1509 } else if member.Name == nodeName { 1510 removeMemberID = member.ID 1511 } else { 1512 // This member is healthy and does not need to be removed. 1513 newClientUrls = append(newClientUrls, member.ClientURLs...) 1514 } 1515 } 1516 et.kvClient.SetEndpoints(newClientUrls...) 1517 et.maintenanceClient.SetEndpoints(newClientUrls...) 1518 1519 if removeMemberID == uint64(0) { 1520 // Member not found. No need to remove it 1521 return nil 1522 } 1523 return et.removeMember(removeMemberID) 1524 } 1525 1526 func (et *etcdKV) removeMember(removeMemberID uint64) error { 1527 fn := "removeMember" 1528 removeMemberRetries := 5 1529 for i := 0; i < removeMemberRetries; i++ { 1530 ctx, cancel := et.MaintenanceContextWithLeader() 1531 _, err := et.kvClient.MemberRemove(ctx, removeMemberID) 1532 cancel() 1533 1534 if err != nil { 1535 // Check if the error is member not found 1536 etcdErr, ok := err.(rpctypes.EtcdError) 1537 if ok && etcdErr == rpctypes.ErrMemberNotFound { 1538 return nil 1539 } 1540 // Check if we need to retry 1541 retry, err := isRetryNeeded(err, fn, "", i) 1542 if !retry { 1543 // For all others return immediately 1544 return err 1545 } 1546 if i == (removeMemberRetries - 1) { 1547 return fmt.Errorf("too many retries for RemoveMember: %v", removeMemberID) 1548 } 1549 time.Sleep(2 * time.Second) 1550 continue 1551 } 1552 break 1553 } 1554 return nil 1555 } 1556 1557 func (et *etcdKV) RemoveMemberByID( 1558 removeMemberID uint64, 1559 ) error { 1560 ctx, cancel := et.MaintenanceContextWithLeader() 1561 memberListResponse, err := et.kvClient.MemberList(ctx) 1562 cancel() 1563 if err != nil { 1564 return err 1565 } 1566 var ( 1567 removeMemberClientURLs, newClientURLs []string 1568 found bool 1569 ) 1570 1571 for _, member := range memberListResponse.Members { 1572 if member.ID == removeMemberID { 1573 found = true 1574 removeMemberClientURLs = append(removeMemberClientURLs, member.ClientURLs...) 1575 break 1576 } 1577 } 1578 if !found { 1579 // Member does not exist 1580 return nil 1581 } 1582 currentEndpoints := et.kvClient.Endpoints() 1583 1584 // Remove the clientURLs for the member which is being removed from 1585 // the active set of endpoints 1586 for _, currentEndpoint := range currentEndpoints { 1587 found := false 1588 for _, removeClientURL := range removeMemberClientURLs { 1589 if removeClientURL == currentEndpoint { 1590 found = true 1591 break 1592 } 1593 } 1594 if !found { 1595 newClientURLs = append(newClientURLs, currentEndpoint) 1596 } 1597 } 1598 1599 et.kvClient.SetEndpoints(newClientURLs...) 1600 et.maintenanceClient.SetEndpoints(newClientURLs...) 1601 1602 return et.removeMember(removeMemberID) 1603 } 1604 1605 func (et *etcdKV) ListMembers() (map[uint64]*kvdb.MemberInfo, error) { 1606 var ( 1607 fnMemberStatus = func(cliURL string) (*kvdb.MemberInfo, uint64, error) { 1608 if cliURL == "" { 1609 return nil, 0, fmt.Errorf("Must provide client URL") 1610 } 1611 // Use the context with no leader requirement as we might be hitting 1612 // an endpoint which is down 1613 ctx, cancel := et.MaintenanceContext() 1614 endpointStatus, err := et.maintenanceClient.Status(ctx, cliURL) 1615 cancel() 1616 1617 if err != nil { 1618 return nil, 0, err 1619 } 1620 1621 mid := endpointStatus.Header.MemberId 1622 return &kvdb.MemberInfo{ 1623 // PeerUrls: .. must fill later 1624 ClientUrls: []string{cliURL}, 1625 Leader: endpointStatus.Leader == mid, 1626 DbSize: endpointStatus.DbSize, 1627 IsHealthy: true, 1628 ID: strconv.FormatUint(mid, 16), 1629 }, mid, nil 1630 } 1631 ) 1632 ctx, cancel := et.MaintenanceContextWithLeader() 1633 memberListResponse, err := et.kvClient.MemberList(ctx) 1634 cancel() 1635 1636 if err != nil { 1637 return nil, err 1638 } 1639 1640 membersMap := make(map[uint64]*kvdb.MemberInfo) 1641 maintenanceClientLock.Lock() 1642 defer maintenanceClientLock.Unlock() 1643 1644 // Get status from Endpoints 1645 for _, ep := range et.GetEndpoints() { 1646 mi, mid, err := fnMemberStatus(ep) 1647 if err != nil || mi == nil { 1648 logrus.WithError(err).Warnf("kvClient.Status(%s) returned error", ep) 1649 continue 1650 } 1651 membersMap[mid] = mi 1652 } 1653 1654 // Get status from MemberList() nodes, that were missing from GetEndpoints() list 1655 for _, member := range memberListResponse.Members { 1656 for _, cu := range member.ClientURLs { 1657 if _, has := membersMap[member.ID]; !has { 1658 1659 mi, mid, err := fnMemberStatus(cu) 1660 if err != nil || mi == nil { 1661 logrus.WithError(err).Warnf("kvClient.Status(%s) returned error", cu) 1662 continue 1663 } 1664 membersMap[mid] = mi 1665 } 1666 } 1667 } 1668 1669 // Fill in other details in the MemberInfo object 1670 for _, member := range memberListResponse.Members { 1671 if mi, has := membersMap[member.ID]; has { 1672 mi.PeerUrls = member.PeerURLs 1673 mi.Name = member.Name 1674 mi.HasStarted = len(member.Name) > 0 1675 mi.IsLearner = member.IsLearner 1676 } else { 1677 // no status -- add "blank" MemberInfo 1678 membersMap[member.ID] = &kvdb.MemberInfo{ 1679 PeerUrls: member.PeerURLs, 1680 Name: member.Name, 1681 HasStarted: len(member.Name) > 0, 1682 ID: strconv.FormatUint(member.ID, 16), 1683 IsLearner: member.IsLearner, 1684 } 1685 } 1686 } 1687 1688 return membersMap, nil 1689 } 1690 1691 func (et *etcdKV) Serialize() ([]byte, error) { 1692 1693 kvps, err := et.Enumerate("") 1694 if err != nil { 1695 return nil, err 1696 } 1697 return et.SerializeAll(kvps) 1698 } 1699 1700 func (et *etcdKV) Deserialize(b []byte) (kvdb.KVPairs, error) { 1701 return et.DeserializeAll(b) 1702 } 1703 1704 func (et *etcdKV) SetEndpoints(endpoints []string) error { 1705 et.kvClient.SetEndpoints(endpoints...) 1706 et.maintenanceClient.SetEndpoints(endpoints...) 1707 return nil 1708 } 1709 1710 func (et *etcdKV) GetEndpoints() []string { 1711 return et.kvClient.Endpoints() 1712 } 1713 1714 func (et *etcdKV) Defragment(endpoint string, timeout int) error { 1715 if timeout < defaultDefragTimeout { 1716 timeout = defaultDefragTimeout 1717 } 1718 1719 ctx, cancel := context.WithTimeout(getContextWithLeaderRequirement(), time.Duration(timeout)*time.Second) 1720 _, err := et.kvClient.Defragment(ctx, endpoint) 1721 cancel() 1722 if err != nil { 1723 logrus.Warnf("defragment operation on %v failed with error: %v", endpoint, err) 1724 return err 1725 } 1726 return nil 1727 1728 } 1729 1730 func (et *etcdKV) listenPeerUrls(ip string, port string) []string { 1731 return []string{et.constructURL(ip, port)} 1732 } 1733 1734 func (et *etcdKV) constructURL(ip string, port string) string { 1735 prefix := urlPrefix 1736 if et.EtcdCommon.IsTLSEnabled() { 1737 prefix = urlSecPrefix 1738 } 1739 ip = strings.TrimPrefix(ip, prefix) 1740 return prefix + ip + ":" + port 1741 } 1742 1743 func (et *etcdKV) getLeaseWithRetries(key string, ttl int64) (*e.LeaseGrantResponse, error) { 1744 var ( 1745 leaseResult *e.LeaseGrantResponse 1746 leaseErr error 1747 retry bool 1748 ) 1749 for i := 0; i < timeoutMaxRetry; i++ { 1750 leaseCtx, leaseCancel := et.LeaseContext() 1751 leaseResult, leaseErr = et.kvClient.Grant(leaseCtx, ttl) 1752 leaseCancel() 1753 if leaseErr != nil { 1754 retry, leaseErr = isRetryNeeded(leaseErr, "lease", key, i) 1755 if !retry { 1756 return nil, leaseErr 1757 } 1758 continue 1759 } 1760 return leaseResult, nil 1761 } 1762 return nil, leaseErr 1763 } 1764 1765 func getContextWithLeaderRequirement() context.Context { 1766 return e.WithRequireLeader(context.Background()) 1767 } 1768 1769 func getEtcdPermType(permType kvdb.PermissionType) (e.PermissionType, error) { 1770 switch permType { 1771 case kvdb.ReadPermission: 1772 return e.PermissionType(e.PermRead), nil 1773 case kvdb.WritePermission: 1774 return e.PermissionType(e.PermWrite), nil 1775 case kvdb.ReadWritePermission: 1776 return e.PermissionType(e.PermReadWrite), nil 1777 default: 1778 return -1, kvdb.ErrUnknownPermission 1779 } 1780 } 1781 1782 // isRetryNeeded checks if for the given error does a kvdb retry required. 1783 // It returns the provided error. 1784 func isRetryNeeded(err error, fn string, key string, retryCount int) (bool, error) { 1785 switch err { 1786 case kvdb.ErrNotSupported, kvdb.ErrWatchStopped, kvdb.ErrNotFound, kvdb.ErrExist, kvdb.ErrUnmarshal, kvdb.ErrValueMismatch, kvdb.ErrModified: 1787 // For all known kvdb errors no retry is needed 1788 return false, err 1789 case rpctypes.ErrGRPCEmptyKey: 1790 return false, kvdb.ErrNotFound 1791 default: 1792 // For all other errors retry 1793 logrus.Errorf("[%v: %v] kvdb error: %v, retry count %v \n", fn, key, err, retryCount) 1794 return true, err 1795 } 1796 } 1797 1798 func rotateEndpointsByOne(endpoints []string) []string { 1799 copy(endpoints, append(endpoints[len(endpoints)-1:], endpoints[:len(endpoints)-1]...)) 1800 return endpoints 1801 }