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  }