github.com/portworx/kvdb@v0.0.0-20241107215734-a185a966f535/consul/kv_consul.go (about) 1 // Package consul implements the KVDB interface based on consul. 2 // Code from docker/libkv was leveraged to build parts of this module. 3 package consul 4 5 import ( 6 "bytes" 7 "encoding/json" 8 "fmt" 9 "math/rand" 10 "regexp" 11 "sort" 12 "strconv" 13 "strings" 14 "sync" 15 "time" 16 17 "github.com/hashicorp/consul/api" 18 "github.com/portworx/kvdb" 19 "github.com/portworx/kvdb/common" 20 "github.com/portworx/kvdb/mem" 21 "github.com/sirupsen/logrus" 22 ) 23 24 const ( 25 // Name is the name of this kvdb implementation. 26 Name = "consul-kv" 27 bootstrap = "kvdb/bootstrap" 28 // MaxRenewRetries to renew TTL. 29 MaxRenewRetries = 5 30 // refreshDelay is the wait to wait before testing connection with a machine 31 refreshDelay = 5 * time.Second 32 ) 33 34 const ( 35 // session ttl limits for consul 36 ttlLowerLimit = 10 // 10 seconds 37 ttlUpperLimit = 60 * 60 * 24 // 1 days 38 ) 39 40 var ( 41 // an incorrect is added to check failover 42 defaultMachines = []string{"3.1.4.1:5926", "127.0.0.1:8500"} 43 ) 44 45 // connectionParam stores connection paramaters for consul kv. 46 type connectionParams struct { 47 // machines is list of consul servers 48 machines []string 49 // options is consul specific options 50 options map[string]string 51 // fatalErrorCb callback to invoke in case of errors 52 fatalErrorCb kvdb.FatalErrorCB 53 } 54 55 // CKVPairs sortable KVPairs 56 type CKVPairs api.KVPairs 57 58 func (c CKVPairs) Len() int { 59 return len(c) 60 } 61 62 func (c CKVPairs) Less(i, j int) bool { 63 return c[i].ModifyIndex < c[j].ModifyIndex 64 } 65 66 func (c CKVPairs) Swap(i, j int) { 67 c[i], c[j] = c[j], c[i] 68 } 69 70 func init() { 71 if err := kvdb.Register(Name, New, Version); err != nil { 72 panic(err.Error()) 73 } 74 } 75 76 func stripConsecutiveForwardslash(key string) string { 77 // Replace consecutive occurences of forward slash with single occurrence 78 re := regexp.MustCompile("(//*)") 79 return re.ReplaceAllString(key, "/") 80 } 81 82 type consulKV struct { 83 common.BaseKvdb 84 // client is an instance of clientConsuler, which is a px defined interface. 85 // clientConsuler wraps pointer to client from consul api but also provides 86 // methods to refresh it during failover. 87 client consulClient 88 domain string 89 kvdb.Controller 90 mu sync.Mutex 91 } 92 93 type consulLock struct { 94 lock *api.Lock 95 doneCh chan struct{} 96 tag interface{} 97 } 98 99 // shuffle list of input strings 100 func shuffle(input []string) []string { 101 tmp := make([]string, len(input)) 102 r := rand.New(rand.NewSource(time.Now().Unix())) 103 for i, j := range r.Perm(len(input)) { 104 tmp[i] = input[j] 105 } 106 return tmp 107 } 108 109 // New constructs a new kvdb.Kvdb given a list of end points to conntect to. 110 func New( 111 domain string, 112 servers []string, 113 options map[string]string, 114 fatalErrorCb kvdb.FatalErrorCB, 115 ) (kvdb.Kvdb, error) { 116 117 // check for unsupported options 118 for _, opt := range []string{kvdb.UsernameKey, kvdb.PasswordKey} { 119 // Check if username provided 120 if _, ok := options[opt]; ok { 121 return nil, kvdb.ErrAuthNotSupported 122 } 123 } 124 125 if domain != "" && !strings.HasSuffix(domain, "/") { 126 domain = domain + "/" 127 } 128 129 hasHttpsPrefix := false 130 for _, machine := range servers { 131 if strings.HasPrefix(machine, "https://") { 132 hasHttpsPrefix = true 133 break 134 } 135 } 136 137 if options == nil { 138 options = make(map[string]string) 139 } 140 141 if hasHttpsPrefix { 142 options[kvdb.TransportScheme] = "https" 143 } else { 144 options[kvdb.TransportScheme] = "http" 145 } 146 147 connParams := connectionParams{ 148 machines: shuffle(servers), 149 options: options, 150 fatalErrorCb: fatalErrorCb, 151 } 152 if len(connParams.machines) == 0 { 153 connParams.machines = defaultMachines 154 } 155 156 var err error 157 var config *api.Config 158 var client *api.Client 159 160 for _, machine := range connParams.machines { 161 if strings.HasPrefix(machine, "http://") { 162 machine = strings.TrimPrefix(machine, "http://") 163 } else if strings.HasPrefix(machine, "https://") { 164 machine = strings.TrimPrefix(machine, "https://") 165 } 166 if config, client, err = newKvClient(machine, connParams); err == nil { 167 return &consulKV{ 168 BaseKvdb: common.BaseKvdb{FatalCb: connParams.fatalErrorCb, LockTryDuration: kvdb.DefaultLockTryDuration}, 169 domain: domain, 170 Controller: kvdb.ControllerNotSupported, 171 client: newConsulClient(config, client, refreshDelay, connParams), 172 }, nil 173 } 174 } 175 return nil, err 176 } 177 178 // Version returns the supported version for consul api 179 func Version(url string, kvdbOptions map[string]string) (string, error) { 180 // Currently we support only v1 181 return kvdb.ConsulVersion1, nil 182 } 183 184 func (kv *consulKV) String() string { 185 return Name 186 } 187 188 func (kv *consulKV) Capabilities() int { 189 return 0 190 } 191 192 func (kv *consulKV) Get(key string) (*kvdb.KVPair, error) { 193 options := &api.QueryOptions{ 194 AllowStale: false, 195 RequireConsistent: true, 196 } 197 key = kv.domain + key 198 key = stripConsecutiveForwardslash(key) 199 200 pair, meta, err := kv.client.Get(key, options) 201 if err != nil { 202 return nil, err 203 } 204 205 if pair == nil { 206 return nil, kvdb.ErrNotFound 207 } 208 return kv.pairToKv("get", pair, meta), nil 209 } 210 211 func (kv *consulKV) GetVal(key string, val interface{}) (*kvdb.KVPair, error) { 212 kvp, err := kv.Get(key) 213 if err != nil { 214 return nil, err 215 } 216 return kvp, json.Unmarshal(kvp.Value, val) 217 } 218 219 func (kv *consulKV) createTTLSession( 220 key string, 221 val interface{}, 222 ttl uint64, 223 noCreate bool, 224 ) (*api.KVPair, error) { 225 pathKey := kv.domain + key 226 pathKey = stripConsecutiveForwardslash(pathKey) 227 b, err := common.ToBytes(val) 228 if err != nil { 229 return nil, err 230 } 231 pair := &api.KVPair{ 232 Key: pathKey, 233 Value: b, 234 } 235 236 if ttl > 0 { 237 if ttl < ttlLowerLimit*2 { // multiply by 2 because we divide ttl values later by 2 238 return nil, kvdb.ErrTTLNotSupported 239 } 240 if ttl > ttlUpperLimit*2 { 241 return nil, kvdb.ErrTTLNotSupported 242 } 243 // Future Use : To Support TTL values 244 for retries := 1; retries <= MaxRenewRetries; retries++ { 245 // Consul doubles the ttl value. Hence we divide it by 2 246 // Consul does not support ttl values less than 10. 247 // Hence we set our lower limit to 20. 248 // Consul does not support ttl values more than 1 day. 249 // Hence we set our upper limit to 2 days. 250 session, err := kv.renewSession(pair, ttl/2, noCreate) 251 if err == nil { 252 pair.Session = session 253 break 254 } 255 if retries == MaxRenewRetries { 256 return nil, kvdb.ErrSetTTLFailed 257 } 258 } 259 } 260 return pair, nil 261 } 262 263 func (kv *consulKV) Put( 264 key string, 265 val interface{}, 266 ttl uint64, 267 ) (*kvdb.KVPair, error) { 268 pair, err := kv.createTTLSession(key, val, ttl, false) 269 if err != nil { 270 return nil, err 271 } 272 273 if ttl == 0 { 274 275 if _, err := kv.client.Put(pair, nil); err != nil { 276 return nil, err 277 } 278 } else { 279 // It is unclear why err == nil but ok == false. We always 280 // delete any existing sessions on Put, so this should work fine. 281 if _, err := kv.client.Acquire(pair, nil); err != nil { 282 return nil, err 283 } 284 } 285 286 kvPair, err := kv.Get(key) 287 if err != nil { 288 return nil, err 289 } 290 kvPair.Action = kvdb.KVSet 291 return kvPair, nil 292 } 293 294 func (kv *consulKV) Create( 295 key string, 296 val interface{}, 297 ttl uint64, 298 ) (*kvdb.KVPair, error) { 299 sessionPair, err := kv.createTTLSession(key, val, ttl, true) 300 if err != nil { 301 return nil, err 302 } 303 kvPair := &kvdb.KVPair{Key: key, Value: sessionPair.Value} 304 kvPair, err = kv.CompareAndSet(kvPair, kvdb.KVModifiedIndex, nil) 305 if err == nil { 306 kvPair.Action = kvdb.KVCreate 307 if ttl > 0 { 308 if _, ok, err := kv.client.CreateMeta(key, sessionPair, nil); ok && err == nil { 309 return kvPair, err 310 } else if err != nil { 311 return nil, err 312 } 313 } 314 } 315 if err == kvdb.ErrModified { 316 // key already exists since compare and set with index 0 failed. 317 err = kvdb.ErrExist 318 } 319 return kvPair, err 320 } 321 322 func (kv *consulKV) Update( 323 key string, 324 val interface{}, 325 ttl uint64, 326 ) (*kvdb.KVPair, error) { 327 if _, err := kv.Get(key); err != nil { 328 return nil, err 329 } 330 331 kvPair, err := kv.Put(key, val, ttl) 332 if err != nil { 333 return nil, err 334 } 335 336 kvPair.Action = kvdb.KVSet 337 return kvPair, nil 338 } 339 340 func (kv *consulKV) Enumerate(prefix string) (kvdb.KVPairs, error) { 341 prefix = kv.domain + prefix 342 prefix = stripConsecutiveForwardslash(prefix) 343 344 pairs, meta, err := kv.client.List(prefix, nil) 345 if err != nil { 346 return nil, err 347 } 348 349 return kv.pairToKvs("enumerate", pairs, meta), nil 350 } 351 352 func (kv *consulKV) Delete(key string) (*kvdb.KVPair, error) { 353 pair, err := kv.Get(key) 354 if err != nil { 355 return nil, err 356 } 357 key = kv.domain + key 358 key = stripConsecutiveForwardslash(key) 359 360 if _, err := kv.client.Delete(key, nil); err != nil { 361 return nil, err 362 } 363 364 return pair, nil 365 } 366 367 func (kv *consulKV) DeleteTree(key string) error { 368 key = kv.domain + key 369 key = stripConsecutiveForwardslash(key) 370 if !strings.HasSuffix(key, kvdb.DefaultSeparator) { 371 key += kvdb.DefaultSeparator 372 } 373 374 _, err := kv.client.DeleteTree(key, nil) 375 return err 376 } 377 378 func (kv *consulKV) Keys(prefix, sep string) ([]string, error) { 379 if "" == sep { 380 sep = "/" 381 } 382 prefix = kv.domain + prefix 383 prefix = stripConsecutiveForwardslash(prefix) 384 lenPrefix := len(prefix) 385 lenSep := len(sep) 386 if prefix[lenPrefix-lenSep:] != sep { 387 prefix += sep 388 lenPrefix += lenSep 389 } 390 391 list, _, err := kv.client.Keys(prefix, sep, nil) 392 if err != nil { 393 return nil, err 394 } 395 396 var retList []string 397 if len(list) > 0 { 398 retList = make([]string, len(list)) 399 for i, key := range list { 400 if strings.HasPrefix(key, prefix) { 401 key = key[lenPrefix:] 402 } 403 if lky := len(key); lky > lenSep && key[lky-lenSep:] == sep { 404 key = key[0 : lky-lenSep] 405 } 406 retList[i] = key 407 } 408 } 409 return retList, nil 410 } 411 412 func (kv *consulKV) CompareAndSet( 413 kvp *kvdb.KVPair, 414 flags kvdb.KVFlags, 415 prevValue []byte, 416 ) (*kvdb.KVPair, error) { 417 key := kv.domain + kvp.Key 418 key = stripConsecutiveForwardslash(key) 419 pair := &api.KVPair{ 420 Key: key, 421 Value: kvp.Value, 422 Flags: api.LockFlagValue, 423 } 424 if (flags & kvdb.KVModifiedIndex) != 0 { 425 pair.ModifyIndex = kvp.ModifiedIndex 426 } else if (flags&kvdb.KVModifiedIndex) == 0 && prevValue != nil { 427 kvPair, err := kv.Get(kvp.Key) 428 if err != nil { 429 return nil, err 430 } 431 432 // Prev Value not equal to current value in etcd 433 if bytes.Compare(kvPair.Value, prevValue) != 0 { 434 return nil, kvdb.ErrValueMismatch 435 } 436 pair.ModifyIndex = kvPair.ModifiedIndex 437 } else { 438 pair.ModifyIndex = 0 439 } 440 441 ok, _, err := kv.client.CompareAndSet(kvp.Key, kvp.Value, pair, nil) 442 if err != nil { 443 return nil, err 444 } 445 446 if !ok { 447 kvp, getErr := kv.Get(pair.Key) 448 if getErr == nil { 449 if bytes.Compare(kvp.Value, pair.Value) == 0 { 450 return kvp, nil 451 } 452 } 453 if (flags & kvdb.KVModifiedIndex) == 0 { 454 return nil, kvdb.ErrValueMismatch 455 } 456 return nil, kvdb.ErrModified 457 } 458 459 kvPair, err := kv.Get(kvp.Key) 460 if err != nil { 461 return nil, err 462 } 463 return kvPair, nil 464 } 465 466 func (kv *consulKV) CompareAndDelete( 467 kvp *kvdb.KVPair, 468 flags kvdb.KVFlags, 469 ) (*kvdb.KVPair, error) { 470 key := kv.domain + kvp.Key 471 key = stripConsecutiveForwardslash(key) 472 pair := &api.KVPair{ 473 Key: key, 474 Value: kvp.Value, 475 Flags: api.LockFlagValue, 476 } 477 478 if (flags & kvdb.KVModifiedIndex) == 0 { 479 // Use value for comparison 480 kvPair, err := kv.Get(kvp.Key) 481 if err != nil { 482 return nil, err 483 } 484 485 // Prev Value not equal to current value in etcd 486 if bytes.Compare(kvPair.Value, kvp.Value) != 0 { 487 return nil, kvdb.ErrValueMismatch 488 } 489 pair.ModifyIndex = kvPair.ModifiedIndex 490 } else { 491 // Use index for comparison 492 pair.ModifyIndex = kvp.ModifiedIndex 493 } 494 495 ok, _, err := kv.client.CompareAndDelete(kvp.Key, kvp.Value, pair, nil) 496 if err != nil { 497 return nil, err 498 } 499 500 if !ok { 501 return nil, kvdb.ErrModified 502 } 503 return kvp, nil 504 } 505 506 func (kv *consulKV) WatchKey( 507 key string, 508 waitIndex uint64, 509 opaque interface{}, 510 cb kvdb.WatchCB, 511 ) error { 512 var keyExist bool 513 kvp, err := kv.Get(key) 514 if err == kvdb.ErrNotFound { 515 keyExist = false 516 } else if err != nil { 517 return err 518 } else { 519 keyExist = true 520 } 521 522 if waitIndex == 0 && kvp != nil { 523 waitIndex = kvp.KVDBIndex 524 } 525 526 key = kv.domain + key 527 go kv.watchKeyStart(key, keyExist, waitIndex, opaque, cb) 528 return nil 529 } 530 531 func (kv *consulKV) WatchTree(prefix string, waitIndex uint64, opaque interface{}, cb kvdb.WatchCB) error { 532 var prefixExist bool 533 kvps, err := kv.Enumerate(prefix) 534 if err == kvdb.ErrNotFound { 535 prefixExist = false 536 } else if err != nil { 537 return err 538 } else { 539 prefixExist = true 540 } 541 542 if waitIndex == 0 && kvps != nil && len(kvps) != 0 { 543 waitIndex = kvps[0].KVDBIndex 544 } 545 546 prefix = kv.domain + prefix 547 go kv.watchTreeStart(prefix, prefixExist, waitIndex, opaque, cb) 548 return nil 549 } 550 551 func (kv *consulKV) Compact(index uint64) error { 552 return kvdb.ErrNotSupported 553 } 554 555 func (kv *consulKV) Lock(key string) (*kvdb.KVPair, error) { 556 return kv.LockWithID(key, "locked") 557 } 558 559 func (kv *consulKV) LockWithID(key string, lockerID string) ( 560 *kvdb.KVPair, 561 error, 562 ) { 563 return kv.LockWithTimeout(key, lockerID, kv.LockTryDuration, kv.GetLockHoldDuration()) 564 } 565 566 func (kv *consulKV) LockWithTimeout( 567 key string, 568 lockerID string, 569 lockTryDuration time.Duration, 570 lockHoldDuration time.Duration, 571 ) (*kvdb.KVPair, error) { 572 key = stripConsecutiveForwardslash(key) 573 // Strip of the leading slash or else consul throws error 574 if key[0] == '/' { 575 key = key[1:] 576 } 577 578 timeout := time.After(lockTryDuration) 579 var l *consulLock 580 err := fmt.Errorf("Timeout acquiring lock") 581 done := false 582 for !done { 583 select { 584 case <-timeout: 585 return nil, err 586 default: 587 l, err = kv.getLock(key, lockerID, lockHoldDuration) 588 if err == nil { 589 done = true 590 } else { 591 time.Sleep(time.Second) 592 } 593 } 594 } 595 return &kvdb.KVPair{ 596 Key: key, 597 Lock: l, 598 }, nil 599 } 600 601 func (kv *consulKV) IsKeyLocked(key string) (bool, string, error) { 602 kvPair, err := kv.Get(key) 603 if err == kvdb.ErrNotFound { 604 return false, "", nil 605 } else if err != nil { 606 return false, "", err 607 } 608 lockerID := string(kvPair.Value) 609 return true, lockerID, nil 610 } 611 612 func (kv *consulKV) Unlock(kvp *kvdb.KVPair) error { 613 l, ok := kvp.Lock.(*consulLock) 614 if !ok { 615 return fmt.Errorf("Invalid lock structure for key: %v", string(kvp.Key)) 616 } 617 _, err := kv.Delete(kvp.Key) 618 619 if err == nil || isConsulErrNeedingRetry(err) { 620 _ = l.lock.Unlock() 621 // stop refreshing the lock, this will automatically release the lock 622 if l.doneCh != nil { 623 close(l.doneCh) 624 } 625 return nil 626 } 627 logrus.Errorf("Unlock failed for key: %s, tag: %s, error: %s", kvp.Key, 628 l.tag, err.Error()) 629 return err 630 } 631 632 func (kv *consulKV) TxNew() (kvdb.Tx, error) { 633 return nil, kvdb.ErrNotSupported 634 } 635 636 func (kv *consulKV) Snapshot(prefixes []string, consistent bool) (kvdb.Kvdb, uint64, error) { 637 if len(prefixes) == 0 { 638 prefixes = []string{""} 639 } else { 640 prefixes = append(prefixes, bootstrap) 641 prefixes = common.PrunePrefixes(prefixes) 642 } 643 644 // Create a new bootstrap key : lowest index 645 r := rand.New(rand.NewSource(time.Now().UnixNano())).Int63() 646 bootStrapKeyLow := bootstrap + strconv.FormatInt(r, 10) + 647 strconv.FormatInt(time.Now().UnixNano(), 10) 648 val, _ := common.ToBytes(time.Now().UnixNano()) 649 kvPair, err := kv.Put(bootStrapKeyLow, val, 0) 650 if err != nil { 651 return nil, 0, fmt.Errorf("Failed to create snap bootstrap key %v, "+ 652 "err: %v", bootStrapKeyLow, err) 653 } 654 lowestKvdbIndex := kvPair.ModifiedIndex 655 656 options := &api.QueryOptions{ 657 AllowStale: false, 658 RequireConsistent: true, 659 } 660 661 var ( 662 kvPairs kvdb.KVPairs 663 ) 664 for _, prefix := range prefixes { 665 listKey := kv.domain + prefix 666 listKey = stripConsecutiveForwardslash(listKey) 667 668 pairs, _, err := kv.client.List(listKey, options) 669 if err != nil { 670 return nil, 0, err 671 } 672 673 kvps := kv.pairToKvs("enumerate", pairs, nil) 674 kvPairs = append(kvPairs, kvps...) 675 } 676 677 snapDb, err := mem.New( 678 kv.domain, 679 nil, 680 map[string]string{mem.KvSnap: "true"}, 681 kv.FatalCb, 682 ) 683 if err != nil { 684 return nil, 0, err 685 } 686 687 for _, kvPair := range kvPairs { 688 _, err := snapDb.SnapPut(kvPair) 689 if err != nil { 690 return nil, 0, fmt.Errorf("Failed creating snap: %v", err) 691 } 692 } 693 694 if !consistent { 695 // A consistent snapshot is not required 696 // return all the enumerated keys 697 return snapDb, 0, nil 698 } 699 700 // Create bootrap key : highest index 701 bootStrapKeyHigh := bootstrap + strconv.FormatInt(r, 10) + 702 strconv.FormatInt(time.Now().UnixNano(), 10) 703 val, _ = common.ToBytes(time.Now().UnixNano()) 704 705 kvPair, err = kv.Put(bootStrapKeyHigh, val, 0) 706 if err != nil { 707 return nil, 0, fmt.Errorf("Failed to create snap bootstrap key %v, "+ 708 "err: %v", bootStrapKeyHigh, err) 709 } 710 highestKvdbIndex := kvPair.ModifiedIndex 711 712 // In consul Delete does not increment kvdb index. 713 // Hence the put (bootstrap) key and delete both return the same index 714 if lowestKvdbIndex+1 != highestKvdbIndex { 715 // create a watch to get all changes 716 // between lowestKvdbIndex and highestKvdbIndex 717 done := make(chan error) 718 watchClosed := false 719 mutex := &sync.Mutex{} 720 cb := func( 721 prefix string, 722 opaque interface{}, 723 kvp *kvdb.KVPair, 724 err error, 725 ) error { 726 var watchErr error 727 var sendErr error 728 var m *sync.Mutex 729 var found, ok bool 730 731 if err != nil { 732 if err == kvdb.ErrWatchStopped && watchClosed { 733 return nil 734 } 735 watchErr = err 736 sendErr = err 737 goto errordone 738 } 739 740 if kvp == nil { 741 watchErr = fmt.Errorf("kvp is nil") 742 sendErr = watchErr 743 goto errordone 744 745 } 746 747 m, ok = opaque.(*sync.Mutex) 748 if !ok { 749 watchErr = fmt.Errorf("Failed to get mutex") 750 sendErr = watchErr 751 goto errordone 752 } 753 754 for _, prefix := range prefixes { 755 if strings.HasPrefix(kvp.Key, prefix) { 756 found = true 757 break 758 } 759 } 760 761 if !found { 762 return nil 763 } 764 765 m.Lock() 766 defer m.Unlock() 767 768 if kvp.ModifiedIndex > highestKvdbIndex { 769 // done applying changes, just return 770 watchErr = fmt.Errorf("done") 771 sendErr = nil 772 goto errordone 773 } else if kvp.ModifiedIndex == highestKvdbIndex { 774 // last update that we needed. Put it inside snap db 775 // and return 776 _, err = snapDb.SnapPut(kvp) 777 if err != nil { 778 watchErr = fmt.Errorf("Failed to apply update to snap: %v", err) 779 sendErr = watchErr 780 } else { 781 watchErr = fmt.Errorf("done") 782 sendErr = nil 783 } 784 goto errordone 785 } else { 786 if kvp.Action == kvdb.KVDelete { 787 _, err = snapDb.Delete(kvp.Key) 788 // A Delete key was issued between our first lowestKvdbIndex Put 789 // and Enumerate APIs in this function 790 if err == kvdb.ErrNotFound { 791 err = nil 792 } 793 794 } else { 795 _, err = snapDb.SnapPut(kvp) 796 } 797 if err != nil { 798 watchErr = fmt.Errorf("Failed to apply update to snap: %v", err) 799 sendErr = watchErr 800 goto errordone 801 } 802 } 803 804 return nil 805 errordone: 806 watchClosed = true 807 done <- sendErr 808 return watchErr 809 } 810 811 if err := kv.WatchTree("", lowestKvdbIndex, mutex, 812 cb); err != nil { 813 return nil, 0, fmt.Errorf("Failed to start watch: %v", err) 814 } 815 err = <-done 816 if err != nil { 817 return nil, 0, err 818 } 819 } 820 821 _, err = kv.Delete(bootStrapKeyLow) 822 if err != nil { 823 return nil, 0, fmt.Errorf("Failed to delete snap bootstrap key: %v, "+ 824 "err: %v", bootStrapKeyLow, err) 825 } 826 _, err = kv.Delete(bootStrapKeyHigh) 827 if err != nil { 828 return nil, 0, fmt.Errorf("Failed to delete snap bootstrap key: %v, "+ 829 "err: %v", bootStrapKeyHigh, err) 830 } 831 return snapDb, highestKvdbIndex, nil 832 } 833 834 func (kv *consulKV) createKv(pair *api.KVPair) *kvdb.KVPair { 835 kvp := &kvdb.KVPair{ 836 Value: []byte(pair.Value), 837 ModifiedIndex: pair.ModifyIndex, 838 CreatedIndex: pair.CreateIndex, 839 } 840 // Strip out the leading '/' 841 if len(pair.Key) != 0 { 842 kvp.Key = pair.Key[1:] 843 } else { 844 kvp.Key = pair.Key 845 } 846 kvp.Key = strings.TrimPrefix(pair.Key, kv.domain) 847 return kvp 848 } 849 850 func (kv *consulKV) EnumerateWithSelect( 851 prefix string, 852 enumerateSelect kvdb.EnumerateSelect, 853 copySelect kvdb.CopySelect, 854 ) ([]interface{}, error) { 855 return nil, kvdb.ErrNotSupported 856 } 857 858 func (kv *consulKV) EnumerateKVPWithSelect( 859 prefix string, 860 enumerateSelect kvdb.EnumerateKVPSelect, 861 copySelect kvdb.CopyKVPSelect, 862 ) (kvdb.KVPairs, error) { 863 return nil, kvdb.ErrNotSupported 864 } 865 866 func (kv *consulKV) GetWithCopy( 867 key string, 868 copySelect kvdb.CopySelect, 869 ) (interface{}, error) { 870 return nil, kvdb.ErrNotSupported 871 } 872 873 func (kv *consulKV) pairToKv(action string, pair *api.KVPair, meta *api.QueryMeta) *kvdb.KVPair { 874 kvp := kv.createKv(pair) 875 switch action { 876 case "create": 877 kvp.Action = kvdb.KVCreate 878 case "set", "update", "put": 879 kvp.Action = kvdb.KVSet 880 case "delete": 881 kvp.Action = kvdb.KVDelete 882 case "get": 883 kvp.Action = kvdb.KVGet 884 default: 885 kvp.Action = kvdb.KVUknown 886 } 887 if meta != nil { 888 kvp.KVDBIndex = meta.LastIndex 889 } 890 return kvp 891 } 892 893 func isHidden(key string) bool { 894 tokens := strings.Split(key, "/") 895 keySuffix := tokens[len(tokens)-1] 896 return keySuffix != "" && keySuffix[0] == '_' 897 } 898 899 func (kv *consulKV) pairToKvs( 900 action string, 901 pairs []*api.KVPair, 902 meta *api.QueryMeta, 903 ) kvdb.KVPairs { 904 kvs := []*kvdb.KVPair{} 905 for _, pair := range pairs { 906 // Ignore hidden keys. 907 if isHidden(pair.Key) { 908 continue 909 } 910 kvs = append(kvs, kv.pairToKv(action, pair, meta)) 911 } 912 return kvs 913 } 914 915 func (kv *consulKV) renewLockSession( 916 key string, 917 initialTTL string, 918 lockTimeout time.Duration, 919 session string, 920 doneCh chan struct{}, 921 tag interface{}, 922 ) { 923 go func() { 924 kv.client.RenewPeriodic(initialTTL, session, nil, doneCh) 925 }() 926 if lockTimeout > 0 { 927 go func() { 928 timeout := time.After(lockTimeout) 929 for { 930 select { 931 case <-timeout: 932 kv.LockTimedout(fmt.Sprintf("Key:%s,Tag:%v", key, tag), lockTimeout) 933 case <-doneCh: 934 return 935 } 936 } 937 }() 938 } 939 } 940 941 func (kv *consulKV) getLock( 942 key string, 943 tag interface{}, 944 lockHoldDuration time.Duration, 945 ) (*consulLock, error) { 946 key = kv.domain + key 947 tagValue, err := common.ToBytes(tag) 948 if err != nil { 949 return nil, fmt.Errorf("Failed to convert tag: %v, error: %v", tag, 950 err) 951 } 952 // Since we need to extend lock hold time, we create a session 953 // which is refreshed every so often until we hit lockHoldDuration, 954 // when we run the FatalCb. Set the TTL to a smaller value so that 955 // the lock is released in case the locking process exits. 956 entry := &api.SessionEntry{ 957 Behavior: api.SessionBehaviorRelease, // Release the lock when the session expires 958 TTL: (10 * time.Second).String(), // Consul multiplies the TTL by 2x 959 LockDelay: 0, // Virtually disable lock delay 960 } 961 962 session, _, err := kv.client.Create(entry, nil) 963 964 // create a lock handle 965 lockOpts := &api.LockOptions{ 966 Key: key, 967 Value: tagValue, 968 LockTryOnce: true, // give up if lock already exists 969 Session: session, 970 LockWaitTime: time.Microsecond, // zero means default, so give a very small value 971 } 972 l, err := kv.client.LockOpts(lockOpts) 973 if err != nil { 974 return nil, err 975 } 976 if lockChan, err := l.Lock(nil); err != nil || lockChan == nil { 977 kv.client.Destroy(session, nil) 978 return nil, kvdb.ErrExist 979 } 980 981 lock := &consulLock{ 982 doneCh: make(chan struct{}), 983 tag: tag, 984 lock: l, 985 } 986 987 kv.renewLockSession(key, entry.TTL, lockHoldDuration, session, lock.doneCh, tag) 988 return lock, nil 989 } 990 991 func (kv *consulKV) watchTreeStart( 992 prefix string, 993 prefixExisted bool, 994 waitIndex uint64, 995 opaque interface{}, 996 cb kvdb.WatchCB, 997 ) { 998 prefix = stripConsecutiveForwardslash(prefix) 999 opts := &api.QueryOptions{ 1000 WaitIndex: waitIndex, 1001 RequireConsistent: true, 1002 } 1003 prefixDeleted := false 1004 prevIndex := uint64(0) 1005 var cbCreateErr, cbUpdateErr error 1006 1007 checkIndex := func(prevIndex *uint64, pair *api.KVPair, newIndex uint64, 1008 msg string, lastIndex, waitIndex uint64) { 1009 if *prevIndex != 0 && newIndex <= *prevIndex { 1010 logrus.Infof(msg+" with index invoked twice: %v, prevIndex: %d"+ 1011 " newIndex: %d, lastIndex: %d, waitIndex: %d", *pair, 1012 *prevIndex, newIndex, lastIndex, waitIndex) 1013 } 1014 *prevIndex = newIndex 1015 } 1016 1017 for { 1018 // Make a blocking List query 1019 kvPairs, meta, err := kv.client.List(prefix, opts) 1020 1021 pairs := CKVPairs(kvPairs) 1022 sort.Sort(pairs) 1023 if err != nil { 1024 logrus.Errorf("Consul returned an error : %s\n", err.Error()) 1025 cbUpdateErr = cb(prefix, opaque, nil, err) 1026 } else if pairs == nil && prefixExisted && !prefixDeleted { 1027 // Got a delete on the prefix of the tree (Last Key under the tree being deleted) 1028 pair := &api.KVPair{ 1029 Key: prefix, 1030 Value: nil, 1031 } 1032 kvPair := kv.pairToKv("delete", pair, meta) 1033 1034 if meta != nil { 1035 kvPair.ModifiedIndex = meta.LastIndex 1036 checkIndex(&prevIndex, pair, kvPair.ModifiedIndex, 1037 "delete", meta.LastIndex, opts.WaitIndex) 1038 // Set the wait index so that we block on the next List call 1039 opts.WaitIndex = meta.LastIndex 1040 } else { 1041 checkIndex(&prevIndex, pair, kvPair.ModifiedIndex, 1042 "delete", 0, opts.WaitIndex) 1043 } 1044 1045 // Callback with a delete action 1046 cbUpdateErr = cb(prefix, opaque, kvPair, nil) 1047 prefixDeleted = true 1048 1049 } else if pairs == nil && !prefixExisted { 1050 // Prefix Never existed or hasn't been created yet 1051 continue 1052 } else if pairs == nil && prefixDeleted { 1053 // Prefix has been deleted and this is a recurring list. 1054 // Do not execute callback 1055 // Set the waitIndex so that we block on the next Get call 1056 opts.WaitIndex = meta.LastIndex 1057 continue 1058 } else { 1059 // Same waitIndex as previous. Out of blocking call because 1060 // waitTime timeouted. (This should not happen) 1061 if opts.WaitIndex >= meta.LastIndex { 1062 continue 1063 } 1064 // Find the key value pair(s) that was(were) added/modified/deleted 1065 found := false 1066 for _, pair := range pairs { 1067 // Check if pair's ModifyIndex lies between the wait index and the last modified index 1068 if pair.ModifyIndex > opts.WaitIndex { 1069 if pair.CreateIndex == pair.ModifyIndex { 1070 // Callback with a create action 1071 checkIndex(&prevIndex, pair, pair.CreateIndex, 1072 "Create", meta.LastIndex, opts.WaitIndex) 1073 cbCreateErr = cb(prefix, opaque, kv.pairToKv("create", pair, meta), nil) 1074 prefixDeleted = false 1075 prefixExisted = true 1076 } else if (pair.CreateIndex > opts.WaitIndex) && (pair.ModifyIndex > pair.CreateIndex) { 1077 // In this single update from consul we have got both a create action and 1078 // update action for this kvpair. Calling two callback functions with different actions 1079 checkIndex(&prevIndex, pair, pair.ModifyIndex, 1080 "Create", meta.LastIndex, opts.WaitIndex) 1081 cbCreateErr = cb(prefix, opaque, kv.pairToKv("create", pair, meta), nil) 1082 prefixDeleted = false 1083 prefixExisted = true 1084 // Callback with an update action 1085 cbUpdateErr = cb(prefix, opaque, kv.pairToKv("update", pair, meta), nil) 1086 } else { 1087 // Callback with an update action 1088 checkIndex(&prevIndex, pair, pair.ModifyIndex, 1089 "Update", meta.LastIndex, opts.WaitIndex) 1090 cbUpdateErr = cb(prefix, opaque, kv.pairToKv("update", pair, meta), nil) 1091 } 1092 found = true 1093 } 1094 } 1095 if found != true { 1096 // We had a sub-key delete 1097 pair := &api.KVPair{ 1098 Key: prefix, 1099 Value: nil, 1100 } 1101 kvPair := kv.pairToKv("delete", pair, meta) 1102 kvPair.ModifiedIndex = meta.LastIndex 1103 checkIndex(&prevIndex, pair, kvPair.ModifiedIndex, "delete", 1104 meta.LastIndex, opts.WaitIndex) 1105 cbUpdateErr = cb(prefix, opaque, kvPair, nil) 1106 } 1107 // Set the waitIndex so that we block on the next List call 1108 opts.WaitIndex = meta.LastIndex 1109 } 1110 if cbUpdateErr != nil || cbCreateErr != nil { 1111 _ = cb(prefix, opaque, nil, kvdb.ErrWatchStopped) 1112 break 1113 } 1114 } 1115 } 1116 1117 func (kv *consulKV) watchKeyStart( 1118 key string, 1119 keyExisted bool, 1120 waitIndex uint64, 1121 opaque interface{}, 1122 cb kvdb.WatchCB, 1123 ) { 1124 key = stripConsecutiveForwardslash(key) 1125 opts := &api.QueryOptions{ 1126 WaitIndex: waitIndex, 1127 } 1128 1129 // Flags used to detect a deleted key 1130 keyDeleted := false 1131 var cbErr error 1132 for { 1133 pair, meta, err := kv.client.Get(key, opts) 1134 1135 if err != nil { 1136 logrus.Errorf("get: Consul returned an error : %s\n", err.Error()) 1137 cbErr = cb(key, opaque, nil, err) 1138 } else if pair == nil && keyExisted && !keyDeleted { 1139 // Key being Deleted for the first time after its creation 1140 pair = &api.KVPair{ 1141 Key: key, 1142 Value: nil, 1143 } 1144 kvPair := kv.pairToKv("delete", pair, meta) 1145 kvPair.ModifiedIndex = meta.LastIndex 1146 1147 // Callback with a delete action 1148 cbErr = cb(key, opaque, kvPair, nil) 1149 1150 // Set the waitIndex so that we block on the next Get call 1151 opts.WaitIndex = meta.LastIndex 1152 keyDeleted = true 1153 } else if pair == nil && !keyExisted { 1154 // Key Never existed or hasn't been created yet 1155 // Set the waitIndex so that we block on the next Get call 1156 opts.WaitIndex = meta.LastIndex 1157 continue 1158 } else if pair == nil && keyDeleted { 1159 // Key has been deleted and this is a recurring get. 1160 // Do not execute callback 1161 // Set the waitIndex so that we block on the next Get call 1162 opts.WaitIndex = meta.LastIndex 1163 continue 1164 } else { 1165 // If LastIndex didn't change it means Get returned because 1166 // of Wait timeout 1167 if opts.WaitIndex == meta.LastIndex { 1168 continue 1169 } 1170 // Set the waitIndex so that we block on the next Get call 1171 opts.WaitIndex = meta.LastIndex 1172 1173 if pair.CreateIndex == pair.ModifyIndex { 1174 // Callback with a create action 1175 cbErr = cb(key, opaque, kv.pairToKv("create", pair, meta), nil) 1176 keyDeleted = false 1177 keyExisted = true 1178 } else { 1179 // Callback with a update action 1180 cbErr = cb(key, opaque, kv.pairToKv("update", pair, meta), nil) 1181 } 1182 } 1183 if cbErr != nil { 1184 _ = cb(key, opaque, nil, kvdb.ErrWatchStopped) 1185 break 1186 } 1187 } 1188 } 1189 1190 // Future Use : Support for ttl values in create/update/put 1191 func (kv *consulKV) renewSession( 1192 pair *api.KVPair, 1193 ttl uint64, 1194 noCreate bool, 1195 ) (string, error) { 1196 // Check if there is any previous session with an active TTL 1197 session, err := kv.getActiveSession(pair.Key) 1198 if err != nil { 1199 logrus.Infof("Failed to find session: %v", err) 1200 return "", err 1201 } 1202 1203 if session != "" { 1204 if noCreate { 1205 // Do not create new session for the key 1206 return "", kvdb.ErrModified 1207 } 1208 // Destroy the existing session associated with the key 1209 kv.client.Destroy(session, nil) 1210 } 1211 1212 durationTTL := time.Duration(int64(ttl)) * time.Second 1213 1214 entry := &api.SessionEntry{ 1215 Behavior: api.SessionBehaviorDelete, // Delete the key when the session expires 1216 TTL: durationTTL.String(), 1217 LockDelay: 0, // Virtually disable lock delay 1218 } 1219 1220 // Create the key session 1221 session, _, err = kv.client.Create(entry, nil) 1222 1223 // Session timer is started after a call to "Renew" 1224 kv.client.Renew(session, nil) 1225 1226 return session, err 1227 } 1228 1229 // getActiveSession checks if the key already has 1230 // a session attached 1231 func (kv *consulKV) getActiveSession(key string) (string, error) { 1232 pair, _, err := kv.client.Get(key, nil) 1233 if err != nil { 1234 return "", err 1235 } 1236 if pair != nil && pair.Session != "" { 1237 return pair.Session, nil 1238 } 1239 return "", nil 1240 } 1241 1242 func (kv *consulKV) SnapPut(snapKvp *kvdb.KVPair) (*kvdb.KVPair, error) { 1243 return nil, kvdb.ErrNotSupported 1244 } 1245 1246 func (kv *consulKV) AddUser(username string, password string) error { 1247 return kvdb.ErrNotSupported 1248 } 1249 1250 func (kv *consulKV) RemoveUser(username string) error { 1251 return kvdb.ErrNotSupported 1252 } 1253 1254 func (kv *consulKV) GrantUserAccess( 1255 username string, 1256 permType kvdb.PermissionType, 1257 subtree string, 1258 ) error { 1259 return kvdb.ErrNotSupported 1260 } 1261 1262 func (kv *consulKV) RevokeUsersAccess( 1263 username string, 1264 permType kvdb.PermissionType, 1265 subtree string, 1266 ) error { 1267 return kvdb.ErrNotSupported 1268 } 1269 1270 func (kv *consulKV) Serialize() ([]byte, error) { 1271 1272 kvps, err := kv.Enumerate("") 1273 if err != nil { 1274 return nil, err 1275 } 1276 return kv.SerializeAll(kvps) 1277 } 1278 1279 func (kv *consulKV) Deserialize(b []byte) (kvdb.KVPairs, error) { 1280 return kv.DeserializeAll(b) 1281 }