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  }