func (m *member) Metric(metricName string) (string, error) {
	cfgtls := transport.TLSInfo{}
	tr, err := transport.NewTimeoutTransport(cfgtls, time.Second, time.Second, time.Second)
	if err != nil {
		return "", err
	}
	cli := &http.Client{Transport: tr}
	resp, err := cli.Get(m.ClientURLs[0].String() + "/metrics")
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	b, rerr := ioutil.ReadAll(resp.Body)
	if rerr != nil {
		return "", rerr
	}
	lines := strings.Split(string(b), "\n")
	for _, l := range lines {
		if strings.HasPrefix(l, metricName) {
			return strings.Split(l, " ")[1], nil
		}
	}
	return "", nil
}

func ( m * member ) Metric ( metricName string ) ( string , error ) { cfgtls := transport . TLSInfo { } tr , err := transport . NewTimeoutTransport ( cfgtls , time . Second , time . Second , time . Second ) if err != nil { return " " , err } cli := & http . Client { Transport : tr } resp , err := cli . Get ( m . ClientURLs [ 0 ] . String ( ) + " " ) if err != nil { return " " , err } defer resp . Body . Close ( ) b , rerr := ioutil . ReadAll ( resp . Body ) if rerr != nil { return " " , rerr } lines := strings . Split ( string ( b ) , " \n " ) for _ , l := range lines { if strings . HasPrefix ( l , metricName ) { return strings . Split ( l , " " ) [ 1 ] , nil } } return " " , nil }

// Metric gets the metric value for a member


func (m *member) InjectPartition(t testing.TB, others ...*member) {
	for _, other := range others {
		m.s.CutPeer(other.s.ID())
		other.s.CutPeer(m.s.ID())
	}
}

func ( m * member ) InjectPartition ( t testing . TB , others ... * member ) { for _ , other := range others { m . s . CutPeer ( other . s . ID ( ) ) other . s . CutPeer ( m . s . ID ( ) ) } }

// InjectPartition drops connections from m to others, vice versa.

InjectPartition drops connections from m to others vice versa .


func (m *member) RecoverPartition(t testing.TB, others ...*member) {
	for _, other := range others {
		m.s.MendPeer(other.s.ID())
		other.s.MendPeer(m.s.ID())
	}
}

func ( m * member ) RecoverPartition ( t testing . TB , others ... * member ) { for _ , other := range others { m . s . MendPeer ( other . s . ID ( ) ) other . s . MendPeer ( m . s . ID ( ) ) } }

// RecoverPartition recovers connections from m to others, vice versa.

RecoverPartition recovers connections from m to others vice versa .


func NewClusterV3(t testing.TB, cfg *ClusterConfig) *ClusterV3 {
	cfg.UseGRPC = true
	if os.Getenv("CLIENT_DEBUG") != "" {
		clientv3.SetLogger(grpclog.NewLoggerV2WithVerbosity(os.Stderr, os.Stderr, os.Stderr, 4))
	}
	clus := &ClusterV3{
		cluster: NewClusterByConfig(t, cfg),
	}
	clus.Launch(t)

	if !cfg.SkipCreatingClient {
		for _, m := range clus.Members {
			client, err := NewClientV3(m)
			if err != nil {
				t.Fatalf("cannot create client: %v", err)
			}
			clus.clients = append(clus.clients, client)
		}
	}

	return clus
}

func NewClusterV3 ( t testing . TB , cfg * ClusterConfig ) * ClusterV3 { cfg . UseGRPC = true if os . Getenv ( " " ) != " " { clientv3 . SetLogger ( grpclog . NewLoggerV2WithVerbosity ( os . Stderr , os . Stderr , os . Stderr , 4 ) ) } clus := & ClusterV3 { cluster : NewClusterByConfig ( t , cfg ) , } clus . Launch ( t ) if ! cfg . SkipCreatingClient { for _ , m := range clus . Members { client , err := NewClientV3 ( m ) if err != nil { t . Fatalf ( " " , err ) } clus . clients = append ( clus . clients , client ) } } return clus }

// NewClusterV3 returns a launched cluster with a grpc client connection
// for each cluster member.

NewClusterV3 returns a launched cluster with a grpc client connection for each cluster member .


func (opts *jwtOptions) ParseWithDefaults(optMap map[string]string) error {
	if opts.TTL == 0 && optMap[optTTL] == "" {
		opts.TTL = DefaultTTL
	}

	return opts.Parse(optMap)
}

func ( opts * jwtOptions ) ParseWithDefaults ( optMap map [ string ] string ) error { if opts . TTL == 0 && optMap [ optTTL ] == " " { opts . TTL = DefaultTTL } return opts . Parse ( optMap ) }

// ParseWithDefaults will load options from the specified map or set defaults where appropriate


func (opts *jwtOptions) Parse(optMap map[string]string) error {
	var err error
	if ttl := optMap[optTTL]; ttl != "" {
		opts.TTL, err = time.ParseDuration(ttl)
		if err != nil {
			return err
		}
	}

	if file := optMap[optPublicKey]; file != "" {
		opts.PublicKey, err = ioutil.ReadFile(file)
		if err != nil {
			return err
		}
	}

	if file := optMap[optPrivateKey]; file != "" {
		opts.PrivateKey, err = ioutil.ReadFile(file)
		if err != nil {
			return err
		}
	}

	// signing method is a required field
	method := optMap[optSignMethod]
	opts.SignMethod = jwt.GetSigningMethod(method)
	if opts.SignMethod == nil {
		return ErrInvalidAuthMethod
	}

	return nil
}

func
(
opts
*
jwtOptions
)
Parse
(
optMap
map
[
string
]
string
)
error
{
var
err
error
if
ttl
:=
optMap
[
optTTL
]
;
ttl
!=
"
"
{
opts
.
TTL
,
err
=
time
.
ParseDuration
(
ttl
)
if
err
!=
{
return
err
}
}
if
file
:=
optMap
[
optPublicKey
]
;
file
!=
"
"
{
opts
.
PublicKey
,
err
=
ioutil
.
ReadFile
(
file
)
if
err
!=
{
return
err
}
}
if
file
:=
optMap
[
optPrivateKey
]
;
file
!=
"
"
{
opts
.
PrivateKey
,
err
=
ioutil
.
ReadFile
(
file
)
if
err
!=
{
return
err
}
}
// signing method is a required field
method
:=
optMap
[
optSignMethod
]
opts
.
SignMethod
=
jwt
.
GetSigningMethod
(
method
)
if
opts
.
SignMethod
==
{
return
ErrInvalidAuthMethod
}
return
}

// Parse will load options from the specified map


func (opts *jwtOptions) Key() (interface{}, error) {
	switch opts.SignMethod.(type) {
	case *jwt.SigningMethodRSA, *jwt.SigningMethodRSAPSS:
		return opts.rsaKey()
	case *jwt.SigningMethodECDSA:
		return opts.ecKey()
	case *jwt.SigningMethodHMAC:
		return opts.hmacKey()
	default:
		return nil, fmt.Errorf("unsupported signing method: %T", opts.SignMethod)
	}
}

func ( opts * jwtOptions ) Key ( ) ( interface { } , error ) { switch opts . SignMethod . ( type ) { case * jwt . SigningMethodRSA , * jwt . SigningMethodRSAPSS : return opts . rsaKey ( ) case * jwt . SigningMethodECDSA : return opts . ecKey ( ) case * jwt . SigningMethodHMAC : return opts . hmacKey ( ) default : return nil , fmt . Errorf ( " " , opts . SignMethod ) } }

// Key will parse and return the appropriately typed key for the selected signature method


func (h *header) fill(rh *pb.ResponseHeader) {
	if rh == nil {
		plog.Panic("unexpected nil resp.Header")
	}
	rh.ClusterId = uint64(h.clusterID)
	rh.MemberId = uint64(h.memberID)
	rh.RaftTerm = h.sg.Term()
	if rh.Revision == 0 {
		rh.Revision = h.rev()
	}
}

func ( h * header ) fill ( rh * pb . ResponseHeader ) { if rh == nil { plog . Panic ( " " ) } rh . ClusterId = uint64 ( h . clusterID ) rh . MemberId = uint64 ( h . memberID ) rh . RaftTerm = h . sg . Term ( ) if rh . Revision == 0 { rh . Revision = h . rev ( ) } }

// fill populates pb.ResponseHeader using etcdserver information

fill populates pb . ResponseHeader using etcdserver information


func (wb *watchBroadcast) add(w *watcher) bool {
	wb.mu.Lock()
	defer wb.mu.Unlock()
	if wb.nextrev > w.nextrev || (wb.nextrev == 0 && w.nextrev != 0) {
		// wb is too far ahead, w will miss events
		// or wb is being established with a current watcher
		return false
	}
	if wb.responses == 0 {
		// Newly created; create event will be sent by etcd.
		wb.receivers[w] = struct{}{}
		return true
	}
	// already sent by etcd; emulate create event
	ok := w.post(&pb.WatchResponse{
		Header: &pb.ResponseHeader{
			// todo: fill in ClusterId
			// todo: fill in MemberId:
			Revision: w.nextrev,
			// todo: fill in RaftTerm:
		},
		WatchId: w.id,
		Created: true,
	})
	if !ok {
		return false
	}
	wb.receivers[w] = struct{}{}
	watchersCoalescing.Inc()

	return true
}

// add puts a watcher into receiving a broadcast if its revision at least
// meets the broadcast revision. Returns true if added.

add puts a watcher into receiving a broadcast if its revision at least meets the broadcast revision . Returns true if added .


func (ws *watchStream) Watch(id WatchID, key, end []byte, startRev int64, fcs ...FilterFunc) (WatchID, error) {
	// prevent wrong range where key >= end lexicographically
	// watch request with 'WithFromKey' has empty-byte range end
	if len(end) != 0 && bytes.Compare(key, end) != -1 {
		return -1, ErrEmptyWatcherRange
	}

	ws.mu.Lock()
	defer ws.mu.Unlock()
	if ws.closed {
		return -1, ErrEmptyWatcherRange
	}

	if id == AutoWatchID {
		for ws.watchers[ws.nextID] != nil {
			ws.nextID++
		}
		id = ws.nextID
		ws.nextID++
	} else if _, ok := ws.watchers[id]; ok {
		return -1, ErrWatcherDuplicateID
	}

	w, c := ws.watchable.watch(key, end, startRev, id, ws.ch, fcs...)

	ws.cancels[id] = c
	ws.watchers[id] = w
	return id, nil
}

// Watch creates a new watcher in the stream and returns its WatchID.

Watch creates a new watcher in the stream and returns its WatchID .


func newFileEncoder(f *os.File, prevCrc uint32) (*encoder, error) {
	offset, err := f.Seek(0, io.SeekCurrent)
	if err != nil {
		return nil, err
	}
	return newEncoder(f, prevCrc, int(offset)), nil
}

func newFileEncoder ( f * os . File , prevCrc uint32 ) ( * encoder , error ) { offset , err := f . Seek ( 0 , io . SeekCurrent ) if err != nil { return nil , err } return newEncoder ( f , prevCrc , int ( offset ) ) , nil }

// newFileEncoder creates a new encoder with current file offset for the page writer.

newFileEncoder creates a new encoder with current file offset for the page writer .


func purgeFile(lg *zap.Logger, dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}, purgec chan<- string) <-chan error {
	errC := make(chan error, 1)
	go func() {
		for {
			fnames, err := ReadDir(dirname)
			if err != nil {
				errC <- err
				return
			}
			newfnames := make([]string, 0)
			for _, fname := range fnames {
				if strings.HasSuffix(fname, suffix) {
					newfnames = append(newfnames, fname)
				}
			}
			sort.Strings(newfnames)
			fnames = newfnames
			for len(newfnames) > int(max) {
				f := filepath.Join(dirname, newfnames[0])
				l, err := TryLockFile(f, os.O_WRONLY, PrivateFileMode)
				if err != nil {
					break
				}
				if err = os.Remove(f); err != nil {
					errC <- err
					return
				}
				if err = l.Close(); err != nil {
					if lg != nil {
						lg.Warn("failed to unlock/close", zap.String("path", l.Name()), zap.Error(err))
					} else {
						plog.Errorf("error unlocking %s when purging file (%v)", l.Name(), err)
					}
					errC <- err
					return
				}
				if lg != nil {
					lg.Info("purged", zap.String("path", f))
				} else {
					plog.Infof("purged file %s successfully", f)
				}
				newfnames = newfnames[1:]
			}
			if purgec != nil {
				for i := 0; i < len(fnames)-len(newfnames); i++ {
					purgec <- fnames[i]
				}
			}
			select {
			case <-time.After(interval):
			case <-stop:
				return
			}
		}
	}()
	return errC
}

func purgeFile ( lg * zap . Logger , dirname string , suffix string , max uint , interval time . Duration , stop <- chan struct { } , purgec chan <- string ) <- chan error { errC := make ( chan error , 1 ) go func ( ) { for { fnames , err := ReadDir ( dirname ) if err != nil { errC <- err return } newfnames := make ( [ ] string , 0 ) for _ , fname := range fnames { if strings . HasSuffix ( fname , suffix ) { newfnames = append ( newfnames , fname ) } } sort . Strings ( newfnames ) fnames = newfnames for len ( newfnames ) > int ( max ) { f := filepath . Join ( dirname , newfnames [ 0 ] ) l , err := TryLockFile ( f , os . O_WRONLY , PrivateFileMode ) if err != nil { break } if err = os . Remove ( f ) ; err != nil { errC <- err return } if err = l . Close ( ) ; err != nil { if lg != nil { lg . Warn ( " " , zap . String ( " " , l . Name ( ) ) , zap . Error ( err ) ) } else { plog . Errorf ( " " , l . Name ( ) , err ) } errC <- err return } if lg != nil { lg . Info ( " " , zap . String ( " " , f ) ) } else { plog . Infof ( " " , f ) } newfnames = newfnames [ 1 : ] } if purgec != nil { for i := 0 ; i < len ( fnames ) - len ( newfnames ) ; i ++ { purgec <- fnames [ i ] } } select { case <- time . After ( interval ) : case <- stop : return } } } ( ) return errC }

// purgeFile is the internal implementation for PurgeFile which can post purged files to purgec if non-nil.

purgeFile is the internal implementation for PurgeFile which can post purged files to purgec if non - nil .


func (ss *StringsValue) Set(s string) error {
	*ss = strings.Split(s, ",")
	return nil
}

func ( ss * StringsValue ) Set ( s string ) error { * ss = strings . Split ( s , " " ) return nil }

// Set parses a command line set of strings, separated by comma.
// Implements "flag.Value" interface.

Set parses a command line set of strings separated by comma . Implements flag . Value interface .


func NewStringsValue(s string) (ss *StringsValue) {
	if s == "" {
		return &StringsValue{}
	}
	ss = new(StringsValue)
	if err := ss.Set(s); err != nil {
		plog.Panicf("new StringsValue should never fail: %v", err)
	}
	return ss
}

func NewStringsValue ( s string ) ( ss * StringsValue ) { if s == " " { return & StringsValue { } } ss = new ( StringsValue ) if err := ss . Set ( s ) ; err != nil { plog . Panicf ( " " , err ) } return ss }

// NewStringsValue implements string slice as "flag.Value" interface.
// Given value is to be separated by comma.

NewStringsValue implements string slice as flag . Value interface . Given value is to be separated by comma .


func StringsFromFlag(fs *flag.FlagSet, flagName string) []string {
	return []string(*fs.Lookup(flagName).Value.(*StringsValue))
}

func StringsFromFlag ( fs * flag . FlagSet , flagName string ) [ ] string { return [ ] string ( * fs . Lookup ( flagName ) . Value . ( * StringsValue ) ) }

// StringsFromFlag returns a string slice from the flag.

StringsFromFlag returns a string slice from the flag .


func Cluster(v string) string {
	vs := strings.Split(v, ".")
	if len(vs) <= 2 {
		return v
	}
	return fmt.Sprintf("%s.%s", vs[0], vs[1])
}

func Cluster ( v string ) string { vs := strings . Split ( v , " " ) if len ( vs ) <= 2 { return v } return fmt . Sprintf ( " " , vs [ 0 ] , vs [ 1 ] ) }


func NewPageWriter(w io.Writer, pageBytes, pageOffset int) *PageWriter {
	return &PageWriter{
		w:                 w,
		pageOffset:        pageOffset,
		pageBytes:         pageBytes,
		buf:               make([]byte, defaultBufferBytes+pageBytes),
		bufWatermarkBytes: defaultBufferBytes,
	}
}

func NewPageWriter ( w io . Writer , pageBytes , pageOffset int ) * PageWriter { return & PageWriter { w : w , pageOffset : pageOffset , pageBytes : pageBytes , buf : make ( [ ] byte , defaultBufferBytes + pageBytes ) , bufWatermarkBytes : defaultBufferBytes , } }

// NewPageWriter creates a new PageWriter. pageBytes is the number of bytes
// to write per page. pageOffset is the starting offset of io.Writer.

NewPageWriter creates a new PageWriter . pageBytes is the number of bytes to write per page . pageOffset is the starting offset of io . Writer .


func (wh *watcherHub) watch(key string, recursive, stream bool, index, storeIndex uint64) (Watcher, *v2error.Error) {
	reportWatchRequest()
	event, err := wh.EventHistory.scan(key, recursive, index)

	if err != nil {
		err.Index = storeIndex
		return nil, err
	}

	w := &watcher{
		eventChan:  make(chan *Event, 100), // use a buffered channel
		recursive:  recursive,
		stream:     stream,
		sinceIndex: index,
		startIndex: storeIndex,
		hub:        wh,
	}

	wh.mutex.Lock()
	defer wh.mutex.Unlock()
	// If the event exists in the known history, append the EtcdIndex and return immediately
	if event != nil {
		ne := event.Clone()
		ne.EtcdIndex = storeIndex
		w.eventChan <- ne
		return w, nil
	}

	l, ok := wh.watchers[key]

	var elem *list.Element

	if ok { // add the new watcher to the back of the list
		elem = l.PushBack(w)
	} else { // create a new list and add the new watcher
		l = list.New()
		elem = l.PushBack(w)
		wh.watchers[key] = l
	}

	w.remove = func() {
		if w.removed { // avoid removing it twice
			return
		}
		w.removed = true
		l.Remove(elem)
		atomic.AddInt64(&wh.count, -1)
		reportWatcherRemoved()
		if l.Len() == 0 {
			delete(wh.watchers, key)
		}
	}

	atomic.AddInt64(&wh.count, 1)
	reportWatcherAdded()

	return w, nil
}

// Watch function returns a Watcher.
// If recursive is true, the first change after index under key will be sent to the event channel of the watcher.
// If recursive is false, the first change after index at key will be sent to the event channel of the watcher.
// If index is zero, watch will start from the current index + 1.

Watch function returns a Watcher . If recursive is true the first change after index under key will be sent to the event channel of the watcher . If recursive is false the first change after index at key will be sent to the event channel of the watcher . If index is zero watch will start from the current index + 1 .


func (wh *watcherHub) notify(e *Event) {
	e = wh.EventHistory.addEvent(e) // add event into the eventHistory

	segments := strings.Split(e.Node.Key, "/")

	currPath := "/"

	// walk through all the segments of the path and notify the watchers
	// if the path is "/foo/bar", it will notify watchers with path "/",
	// "/foo" and "/foo/bar"

	for _, segment := range segments {
		currPath = path.Join(currPath, segment)
		// notify the watchers who interests in the changes of current path
		wh.notifyWatchers(e, currPath, false)
	}
}

// notify function accepts an event and notify to the watchers.

notify function accepts an event and notify to the watchers .


func (wh *watcherHub) clone() *watcherHub {
	clonedHistory := wh.EventHistory.clone()

	return &watcherHub{
		EventHistory: clonedHistory,
	}
}

func ( wh * watcherHub ) clone ( ) * watcherHub { clonedHistory := wh . EventHistory . clone ( ) return & watcherHub { EventHistory : clonedHistory , } }

// clone function clones the watcherHub and return the cloned one.
// only clone the static content. do not clone the current watchers.

clone function clones the watcherHub and return the cloned one . only clone the static content . do not clone the current watchers .


func isHidden(watchPath, keyPath string) bool {
	// When deleting a directory, watchPath might be deeper than the actual keyPath
	// For example, when deleting /foo we also need to notify watchers on /foo/bar.
	if len(watchPath) > len(keyPath) {
		return false
	}
	// if watch path is just a "/", after path will start without "/"
	// add a "/" to deal with the special case when watchPath is "/"
	afterPath := path.Clean("/" + keyPath[len(watchPath):])
	return strings.Contains(afterPath, "/_")
}

// isHidden checks to see if key path is considered hidden to watch path i.e. the
// last element is hidden or it's within a hidden directory

isHidden checks to see if key path is considered hidden to watch path i . e . the last element is hidden or it s within a hidden directory


func (srv *Server) createEtcdLogFile() error {
	var err error
	srv.etcdLogFile, err = os.Create(srv.Member.Etcd.LogOutputs[0])
	if err != nil {
		return err
	}
	srv.lg.Info("created etcd log file", zap.String("path", srv.Member.Etcd.LogOutputs[0]))
	return nil
}

func ( srv * Server ) createEtcdLogFile ( ) error { var err error srv . etcdLogFile , err = os . Create ( srv . Member . Etcd . LogOutputs [ 0 ] ) if err != nil { return err } srv . lg . Info ( " " , zap . String ( " " , srv . Member . Etcd . LogOutputs [ 0 ] ) ) return nil }


func (srv *Server) runEtcd() error {
	errc := make(chan error)
	go func() {
		time.Sleep(5 * time.Second)
		// server advertise client/peer listener had to start first
		// before setting up proxy listener
		errc <- srv.startProxy()
	}()

	if srv.etcdCmd != nil {
		srv.lg.Info(
			"starting etcd command",
			zap.String("command-path", srv.etcdCmd.Path),
		)
		err := srv.etcdCmd.Start()
		perr := <-errc
		srv.lg.Info(
			"started etcd command",
			zap.String("command-path", srv.etcdCmd.Path),
			zap.Errors("errors", []error{err, perr}),
		)
		if err != nil {
			return err
		}
		return perr
	}

	select {
	case <-srv.etcdServer.Server.ReadyNotify():
		srv.lg.Info("embedded etcd is ready")
	case <-time.After(time.Minute):
		srv.etcdServer.Close()
		return fmt.Errorf("took too long to start %v", <-srv.etcdServer.Err())
	}
	return <-errc
}

// start but do not wait for it to complete


func (srv *Server) stopEtcd(sig os.Signal) error {
	srv.stopProxy()

	if srv.etcdCmd != nil {
		srv.lg.Info(
			"stopping etcd command",
			zap.String("command-path", srv.etcdCmd.Path),
			zap.String("signal", sig.String()),
		)

		err := srv.etcdCmd.Process.Signal(sig)
		if err != nil {
			return err
		}

		errc := make(chan error)
		go func() {
			_, ew := srv.etcdCmd.Process.Wait()
			errc <- ew
			close(errc)
		}()

		select {
		case <-time.After(5 * time.Second):
			srv.etcdCmd.Process.Kill()
		case e := <-errc:
			return e
		}

		err = <-errc

		srv.lg.Info(
			"stopped etcd command",
			zap.String("command-path", srv.etcdCmd.Path),
			zap.String("signal", sig.String()),
			zap.Error(err),
		)
		return err
	}

	srv.lg.Info("stopping embedded etcd")
	srv.etcdServer.Server.HardStop()
	srv.etcdServer.Close()
	srv.lg.Info("stopped embedded etcd")
	return nil
}

func ( srv * Server ) stopEtcd ( sig os . Signal ) error { srv . stopProxy ( ) if srv . etcdCmd != nil { srv . lg . Info ( " " , zap . String ( " " , srv . etcdCmd . Path ) , zap . String ( " " , sig . String ( ) ) , ) err := srv . etcdCmd . Process . Signal ( sig ) if err != nil { return err } errc := make ( chan error ) go func ( ) { _ , ew := srv . etcdCmd . Process . Wait ( ) errc <- ew close ( errc ) } ( ) select { case <- time . After ( 5 * time . Second ) : srv . etcdCmd . Process . Kill ( ) case e := <- errc : return e } err = <- errc srv . lg . Info ( " " , zap . String ( " " , srv . etcdCmd . Path ) , zap . String ( " " , sig . String ( ) ) , zap . Error ( err ) , ) return err } srv . lg . Info ( " " ) srv . etcdServer . Server . HardStop ( ) srv . etcdServer . Close ( ) srv . lg . Info ( " " ) return nil }


func (srv *Server) handle_SIGQUIT_ETCD_AND_REMOVE_DATA_AND_STOP_AGENT() (*rpcpb.Response, error) {
	err := srv.stopEtcd(syscall.SIGQUIT)
	if err != nil {
		return nil, err
	}

	if srv.etcdServer != nil {
		srv.etcdServer.GetLogger().Sync()
	} else {
		srv.etcdLogFile.Sync()
		srv.etcdLogFile.Close()
	}

	err = os.RemoveAll(srv.Member.BaseDir)
	if err != nil {
		return nil, err
	}
	srv.lg.Info("removed base directory", zap.String("dir", srv.Member.BaseDir))

	// stop agent server
	srv.Stop()

	return &rpcpb.Response{
		Success: true,
		Status:  "destroyed etcd and agent",
	}, nil
}

// stop proxy, etcd, delete data directory


func LimitListener(l net.Listener, n int) net.Listener {
	return &limitListener{l, make(chan struct{}, n)}
}

func LimitListener ( l net . Listener , n int ) net . Listener { return & limitListener { l , make ( chan struct { } , n ) } }

// LimitListener returns a Listener that accepts at most n simultaneous
// connections from the provided Listener.

LimitListener returns a Listener that accepts at most n simultaneous connections from the provided Listener .


func allowMethod(w http.ResponseWriter, m string, ms ...string) bool {
	for _, meth := range ms {
		if m == meth {
			return true
		}
	}
	w.Header().Set("Allow", strings.Join(ms, ","))
	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
	return false
}

func allowMethod ( w http . ResponseWriter , m string , ms ... string ) bool { for _ , meth := range ms { if m == meth { return true } } w . Header ( ) . Set ( " " , strings . Join ( ms , " " ) ) http . Error ( w , " " , http . StatusMethodNotAllowed ) return false }

// allowMethod verifies that the given method is one of the allowed methods,
// and if not, it writes an error to w.  A boolean is returned indicating
// whether or not the method is allowed.

allowMethod verifies that the given method is one of the allowed methods and if not it writes an error to w . A boolean is returned indicating whether or not the method is allowed .


func NewWatchServer(s *etcdserver.EtcdServer) pb.WatchServer {
	return &watchServer{
		lg: s.Cfg.Logger,

		clusterID: int64(s.Cluster().ID()),
		memberID:  int64(s.ID()),

		maxRequestBytes: int(s.Cfg.MaxRequestBytes + grpcOverheadBytes),

		sg:        s,
		watchable: s.Watchable(),
		ag:        s,
	}
}

func NewWatchServer ( s * etcdserver . EtcdServer ) pb . WatchServer { return & watchServer { lg : s . Cfg . Logger , clusterID : int64 ( s . Cluster ( ) . ID ( ) ) , memberID : int64 ( s . ID ( ) ) , maxRequestBytes : int ( s . Cfg . MaxRequestBytes + grpcOverheadBytes ) , sg : s , watchable : s . Watchable ( ) , ag : s , } }

// NewWatchServer returns a new watch server.

NewWatchServer returns a new watch server .


func FiltersFromRequest(creq *pb.WatchCreateRequest) []mvcc.FilterFunc {
	filters := make([]mvcc.FilterFunc, 0, len(creq.Filters))
	for _, ft := range creq.Filters {
		switch ft {
		case pb.WatchCreateRequest_NOPUT:
			filters = append(filters, filterNoPut)
		case pb.WatchCreateRequest_NODELETE:
			filters = append(filters, filterNoDelete)
		default:
		}
	}
	return filters
}

func FiltersFromRequest ( creq * pb . WatchCreateRequest ) [ ] mvcc . FilterFunc { filters := make ( [ ] mvcc . FilterFunc , 0 , len ( creq . Filters ) ) for _ , ft := range creq . Filters { switch ft { case pb . WatchCreateRequest_NOPUT : filters = append ( filters , filterNoPut ) case pb . WatchCreateRequest_NODELETE : filters = append ( filters , filterNoDelete ) default : } } return filters }

// FiltersFromRequest returns "mvcc.FilterFunc" from a given watch create request.

FiltersFromRequest returns mvcc . FilterFunc from a given watch create request .


func newPipelineHandler(t *Transport, r Raft, cid types.ID) http.Handler {
	return &pipelineHandler{
		lg:      t.Logger,
		localID: t.ID,
		tr:      t,
		r:       r,
		cid:     cid,
	}
}

func newPipelineHandler ( t * Transport , r Raft , cid types . ID ) http . Handler { return & pipelineHandler { lg : t . Logger , localID : t . ID , tr : t , r : r , cid : cid , } }

// newPipelineHandler returns a handler for handling raft messages
// from pipeline for RaftPrefix.
//
// The handler reads out the raft message from request body,
// and forwards it to the given raft state machine for processing.

newPipelineHandler returns a handler for handling raft messages from pipeline for RaftPrefix . The handler reads out the raft message from request body and forwards it to the given raft state machine for processing .


func checkClusterCompatibilityFromHeader(lg *zap.Logger, localID types.ID, header http.Header, cid types.ID) error {
	remoteName := header.Get("X-Server-From")

	remoteServer := serverVersion(header)
	remoteVs := ""
	if remoteServer != nil {
		remoteVs = remoteServer.String()
	}

	remoteMinClusterVer := minClusterVersion(header)
	remoteMinClusterVs := ""
	if remoteMinClusterVer != nil {
		remoteMinClusterVs = remoteMinClusterVer.String()
	}

	localServer, localMinCluster, err := checkVersionCompatibility(remoteName, remoteServer, remoteMinClusterVer)

	localVs := ""
	if localServer != nil {
		localVs = localServer.String()
	}
	localMinClusterVs := ""
	if localMinCluster != nil {
		localMinClusterVs = localMinCluster.String()
	}

	if err != nil {
		if lg != nil {
			lg.Warn(
				"failed to check version compatibility",
				zap.String("local-member-id", localID.String()),
				zap.String("local-member-cluster-id", cid.String()),
				zap.String("local-member-server-version", localVs),
				zap.String("local-member-server-minimum-cluster-version", localMinClusterVs),
				zap.String("remote-peer-server-name", remoteName),
				zap.String("remote-peer-server-version", remoteVs),
				zap.String("remote-peer-server-minimum-cluster-version", remoteMinClusterVs),
				zap.Error(err),
			)
		} else {
			plog.Errorf("request version incompatibility (%v)", err)
		}
		return errIncompatibleVersion
	}
	if gcid := header.Get("X-Etcd-Cluster-ID"); gcid != cid.String() {
		if lg != nil {
			lg.Warn(
				"request cluster ID mismatch",
				zap.String("local-member-id", localID.String()),
				zap.String("local-member-cluster-id", cid.String()),
				zap.String("local-member-server-version", localVs),
				zap.String("local-member-server-minimum-cluster-version", localMinClusterVs),
				zap.String("remote-peer-server-name", remoteName),
				zap.String("remote-peer-server-version", remoteVs),
				zap.String("remote-peer-server-minimum-cluster-version", remoteMinClusterVs),
				zap.String("remote-peer-cluster-id", gcid),
			)
		} else {
			plog.Errorf("request cluster ID mismatch (got %s want %s)", gcid, cid)
		}
		return errClusterIDMismatch
	}
	return nil
}

func checkClusterCompatibilityFromHeader ( lg * zap . Logger , localID types . ID , header http . Header , cid types . ID ) error { remoteName := header . Get ( " " ) remoteServer := serverVersion ( header ) remoteVs := " " if remoteServer != nil { remoteVs = remoteServer . String ( ) } remoteMinClusterVer := minClusterVersion ( header ) remoteMinClusterVs := " " if remoteMinClusterVer != nil { remoteMinClusterVs = remoteMinClusterVer . String ( ) } localServer , localMinCluster , err := checkVersionCompatibility ( remoteName , remoteServer , remoteMinClusterVer ) localVs := " " if localServer != nil { localVs = localServer . String ( ) } localMinClusterVs := " " if localMinCluster != nil { localMinClusterVs = localMinCluster . String ( ) } if err != nil { if lg != nil { lg . Warn ( " " , zap . String ( " " , localID . String ( ) ) , zap . String ( " " , cid . String ( ) ) , zap . String ( " " , localVs ) , zap . String ( " " , localMinClusterVs ) , zap . String ( " " , remoteName ) , zap . String ( " " , remoteVs ) , zap . String ( " " , remoteMinClusterVs ) , zap . Error ( err ) , ) } else { plog . Errorf ( " " , err ) } return errIncompatibleVersion } if gcid := header . Get ( " " ) ; gcid != cid . String ( ) { if lg != nil { lg . Warn ( " " , zap . String ( " " , localID . String ( ) ) , zap . String ( " " , cid . String ( ) ) , zap . String ( " " , localVs ) , zap . String ( " " , localMinClusterVs ) , zap . String ( " " , remoteName ) , zap . String ( " " , remoteVs ) , zap . String ( " " , remoteMinClusterVs ) , zap . String ( " " , gcid ) , ) } else { plog . Errorf ( " " , gcid , cid ) } return errClusterIDMismatch } return nil }

// checkClusterCompatibilityFromHeader checks the cluster compatibility of
// the local member from the given header.
// It checks whether the version of local member is compatible with
// the versions in the header, and whether the cluster ID of local member
// matches the one in the header.

checkClusterCompatibilityFromHeader checks the cluster compatibility of the local member from the given header . It checks whether the version of local member is compatible with the versions in the header and whether the cluster ID of local member matches the one in the header .


func KeyExists(key string) clientv3.Cmp {
	return clientv3.Compare(clientv3.Version(key), ">", 0)
}

func KeyExists ( key string ) clientv3 . Cmp { return clientv3 . Compare ( clientv3 . Version ( key ) , " " , 0 ) }

// KeyExists returns a comparison operation that evaluates to true iff the given
// key exists. It does this by checking if the key `Version` is greater than 0.
// It is a useful guard in transaction delete operations.

KeyExists returns a comparison operation that evaluates to true iff the given key exists . It does this by checking if the key Version is greater than 0 . It is a useful guard in transaction delete operations .


func KeyMissing(key string) clientv3.Cmp {
	return clientv3.Compare(clientv3.Version(key), "=", 0)
}

func KeyMissing ( key string ) clientv3 . Cmp { return clientv3 . Compare ( clientv3 . Version ( key ) , " " , 0 ) }

// KeyMissing returns a comparison operation that evaluates to true iff the
// given key does not exist.

KeyMissing returns a comparison operation that evaluates to true iff the given key does not exist .


func ValidateSecureEndpoints(tlsInfo TLSInfo, eps []string) ([]string, error) {
	t, err := NewTransport(tlsInfo, 5*time.Second)
	if err != nil {
		return nil, err
	}
	var errs []string
	var endpoints []string
	for _, ep := range eps {
		if !strings.HasPrefix(ep, "https://") {
			errs = append(errs, fmt.Sprintf("%q is insecure", ep))
			continue
		}
		conn, cerr := t.Dial("tcp", ep[len("https://"):])
		if cerr != nil {
			errs = append(errs, fmt.Sprintf("%q failed to dial (%v)", ep, cerr))
			continue
		}
		conn.Close()
		endpoints = append(endpoints, ep)
	}
	if len(errs) != 0 {
		err = fmt.Errorf("%s", strings.Join(errs, ","))
	}
	return endpoints, err
}

func ValidateSecureEndpoints ( tlsInfo TLSInfo , eps [ ] string ) ( [ ] string , error ) { t , err := NewTransport ( tlsInfo , 5 * time . Second ) if err != nil { return nil , err } var errs [ ] string var endpoints [ ] string for _ , ep := range eps { if ! strings . HasPrefix ( ep , " " ) { errs = append ( errs , fmt . Sprintf ( " " , ep ) ) continue } conn , cerr := t . Dial ( " " , ep [ len ( " " ) : ] ) if cerr != nil { errs = append ( errs , fmt . Sprintf ( " " , ep , cerr ) ) continue } conn . Close ( ) endpoints = append ( endpoints , ep ) } if len ( errs ) != 0 { err = fmt . Errorf ( " " , strings . Join ( errs , " " ) ) } return endpoints , err }

// ValidateSecureEndpoints scans the given endpoints against tls info, returning only those
// endpoints that could be validated as secure.

ValidateSecureEndpoints scans the given endpoints against tls info returning only those endpoints that could be validated as secure .


func putNewKV(kv v3.KV, key, val string, leaseID v3.LeaseID) (int64, error) {
	cmp := v3.Compare(v3.Version(key), "=", 0)
	req := v3.OpPut(key, val, v3.WithLease(leaseID))
	txnresp, err := kv.Txn(context.TODO()).If(cmp).Then(req).Commit()
	if err != nil {
		return 0, err
	}
	if !txnresp.Succeeded {
		return 0, ErrKeyExists
	}
	return txnresp.Header.Revision, nil
}

func putNewKV ( kv v3 . KV , key , val string , leaseID v3 . LeaseID ) ( int64 , error ) { cmp := v3 . Compare ( v3 . Version ( key ) , " " , 0 ) req := v3 . OpPut ( key , val , v3 . WithLease ( leaseID ) ) txnresp , err := kv . Txn ( context . TODO ( ) ) . If ( cmp ) . Then ( req ) . Commit ( ) if err != nil { return 0 , err } if ! txnresp . Succeeded { return 0 , ErrKeyExists } return txnresp . Header . Revision , nil }

// putNewKV attempts to create the given key, only succeeding if the key did
// not yet exist.

putNewKV attempts to create the given key only succeeding if the key did not yet exist .


func newUniqueEphemeralKey(s *concurrency.Session, prefix string) (*EphemeralKV, error) {
	return newUniqueEphemeralKV(s, prefix, "")
}

func newUniqueEphemeralKey ( s * concurrency . Session , prefix string ) ( * EphemeralKV , error ) { return newUniqueEphemeralKV ( s , prefix , " " ) }

// newUniqueEphemeralKey creates a new unique valueless key associated with a session lease


func NewUpdateDirCommand() cli.Command {
	return cli.Command{
		Name:      "updatedir",
		Usage:     "update an existing directory",
		ArgsUsage: "<key> <value>",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "ttl", Value: 0, Usage: "key time-to-live in seconds"},
		},
		Action: func(c *cli.Context) error {
			updatedirCommandFunc(c, mustNewKeyAPI(c))
			return nil
		},
	}
}

func NewUpdateDirCommand ( ) cli . Command { return cli . Command { Name : " " , Usage : " " , ArgsUsage : " " , Flags : [ ] cli . Flag { cli . IntFlag { Name : " " , Value : 0 , Usage : " " } , } , Action : func ( c * cli . Context ) error { updatedirCommandFunc ( c , mustNewKeyAPI ( c ) ) return nil } , } }

// NewUpdateDirCommand returns the CLI command for "updatedir".

NewUpdateDirCommand returns the CLI command for updatedir .


func updatedirCommandFunc(c *cli.Context, ki client.KeysAPI) {
	if len(c.Args()) == 0 {
		handleError(c, ExitBadArgs, errors.New("key required"))
	}
	key := c.Args()[0]
	ttl := c.Int("ttl")
	ctx, cancel := contextWithTotalTimeout(c)
	resp, err := ki.Set(ctx, key, "", &client.SetOptions{TTL: time.Duration(ttl) * time.Second, Dir: true, PrevExist: client.PrevExist})
	cancel()
	if err != nil {
		handleError(c, ExitServerError, err)
	}
	if c.GlobalString("output") != "simple" {
		printResponseKey(resp, c.GlobalString("output"))
	}
}

func updatedirCommandFunc ( c * cli . Context , ki client . KeysAPI ) { if len ( c . Args ( ) ) == 0 { handleError ( c , ExitBadArgs , errors . New ( " " ) ) } key := c . Args ( ) [ 0 ] ttl := c . Int ( " " ) ctx , cancel := contextWithTotalTimeout ( c ) resp , err := ki . Set ( ctx , key , " " , & client . SetOptions { TTL : time . Duration ( ttl ) * time . Second , Dir : true , PrevExist : client . PrevExist } ) cancel ( ) if err != nil { handleError ( c , ExitServerError , err ) } if c . GlobalString ( " " ) != " " { printResponseKey ( resp , c . GlobalString ( " " ) ) } }

// updatedirCommandFunc executes the "updatedir" command.

updatedirCommandFunc executes the updatedir command .


func handleBackup(c *cli.Context) error {
	var srcWAL string
	var destWAL string

	withV3 := c.Bool("with-v3")
	srcSnap := filepath.Join(c.String("data-dir"), "member", "snap")
	destSnap := filepath.Join(c.String("backup-dir"), "member", "snap")

	if c.String("wal-dir") != "" {
		srcWAL = c.String("wal-dir")
	} else {
		srcWAL = filepath.Join(c.String("data-dir"), "member", "wal")
	}

	if c.String("backup-wal-dir") != "" {
		destWAL = c.String("backup-wal-dir")
	} else {
		destWAL = filepath.Join(c.String("backup-dir"), "member", "wal")
	}

	if err := fileutil.CreateDirAll(destSnap); err != nil {
		log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err)
	}

	walsnap := saveSnap(destSnap, srcSnap)
	metadata, state, ents := loadWAL(srcWAL, walsnap, withV3)
	saveDB(filepath.Join(destSnap, "db"), filepath.Join(srcSnap, "db"), state.Commit, withV3)

	idgen := idutil.NewGenerator(0, time.Now())
	metadata.NodeID = idgen.Next()
	metadata.ClusterID = idgen.Next()

	neww, err := wal.Create(zap.NewExample(), destWAL, pbutil.MustMarshal(&metadata))
	if err != nil {
		log.Fatal(err)
	}
	defer neww.Close()
	if err := neww.Save(state, ents); err != nil {
		log.Fatal(err)
	}
	if err := neww.SaveSnapshot(walsnap); err != nil {
		log.Fatal(err)
	}

	return nil
}

func handleBackup ( c * cli . Context ) error { var srcWAL string var destWAL string withV3 := c . Bool ( " " ) srcSnap := filepath . Join ( c . String ( " " ) , " " , " " ) destSnap := filepath . Join ( c . String ( " " ) , " " , " " ) if c . String ( " " ) != " " { srcWAL = c . String ( " " ) } else { srcWAL = filepath . Join ( c . String ( " " ) , " " , " " ) } if c . String ( " " ) != " " { destWAL = c . String ( " " ) } else { destWAL = filepath . Join ( c . String ( " " ) , " " , " " ) } if err := fileutil . CreateDirAll ( destSnap ) ; err != nil { log . Fatalf ( " " , destSnap , err ) } walsnap := saveSnap ( destSnap , srcSnap ) metadata , state , ents := loadWAL ( srcWAL , walsnap , withV3 ) saveDB ( filepath . Join ( destSnap , " " ) , filepath . Join ( srcSnap , " " ) , state . Commit , withV3 ) idgen := idutil . NewGenerator ( 0 , time . Now ( ) ) metadata . NodeID = idgen . Next ( ) metadata . ClusterID = idgen . Next ( ) neww , err := wal . Create ( zap . NewExample ( ) , destWAL , pbutil . MustMarshal ( & metadata ) ) if err != nil { log . Fatal ( err ) } defer neww . Close ( ) if err := neww . Save ( state , ents ) ; err != nil { log . Fatal ( err ) } if err := neww . SaveSnapshot ( walsnap ) ; err != nil { log . Fatal ( err ) } return nil }

// handleBackup handles a request that intends to do a backup.

handleBackup handles a request that intends to do a backup .


func saveDB(destDB, srcDB string, idx uint64, v3 bool) {
	// open src db to safely copy db state
	if v3 {
		var src *bolt.DB
		ch := make(chan *bolt.DB, 1)
		go func() {
			db, err := bolt.Open(srcDB, 0444, &bolt.Options{ReadOnly: true})
			if err != nil {
				log.Fatal(err)
			}
			ch <- db
		}()
		select {
		case src = <-ch:
		case <-time.After(time.Second):
			log.Println("waiting to acquire lock on", srcDB)
			src = <-ch
		}
		defer src.Close()

		tx, err := src.Begin(false)
		if err != nil {
			log.Fatal(err)
		}

		// copy srcDB to destDB
		dest, err := os.Create(destDB)
		if err != nil {
			log.Fatal(err)
		}
		if _, err := tx.WriteTo(dest); err != nil {
			log.Fatal(err)
		}
		dest.Close()
		if err := tx.Rollback(); err != nil {
			log.Fatal(err)
		}
	}

	db, err := bolt.Open(destDB, 0644, &bolt.Options{})
	if err != nil {
		log.Fatal(err)
	}
	tx, err := db.Begin(true)
	if err != nil {
		log.Fatal(err)
	}

	// remove membership information; should be clobbered by --force-new-cluster
	for _, bucket := range []string{"members", "members_removed", "cluster"} {
		tx.DeleteBucket([]byte(bucket))
	}

	// update consistent index to match hard state
	if !v3 {
		idxBytes := make([]byte, 8)
		binary.BigEndian.PutUint64(idxBytes, idx)
		b, err := tx.CreateBucketIfNotExists([]byte("meta"))
		if err != nil {
			log.Fatal(err)
		}
		b.Put([]byte("consistent_index"), idxBytes)
	}

	if err := tx.Commit(); err != nil {
		log.Fatal(err)
	}
	if err := db.Close(); err != nil {
		log.Fatal(err)
	}
}

// saveDB copies the v3 backend and strips cluster information.

saveDB copies the v3 backend and strips cluster information .


func NewWatchCommand() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "watcher",
		Short: "Performs watch operation",
		Run:   runWatcherFunc,
	}
	cmd.Flags().DurationVar(&runningTime, "running-time", 60, "number of seconds to run")
	cmd.Flags().StringVar(&watchPrefix, "prefix", "", "the prefix to append on all keys")
	cmd.Flags().IntVar(&noOfPrefixes, "total-prefixes", 10, "total no of prefixes to use")
	cmd.Flags().IntVar(&watchPerPrefix, "watch-per-prefix", 10, "number of watchers per prefix")
	cmd.Flags().IntVar(&totalKeys, "total-keys", 1000, "total number of keys to watch")

	return cmd
}

func NewWatchCommand ( ) * cobra . Command { cmd := & cobra . Command { Use : " " , Short : " " , Run : runWatcherFunc , } cmd . Flags ( ) . DurationVar ( & runningTime , " " , 60 , " " ) cmd . Flags ( ) . StringVar ( & watchPrefix , " " , " " , " " ) cmd . Flags ( ) . IntVar ( & noOfPrefixes , " " , 10 , " " ) cmd . Flags ( ) . IntVar ( & watchPerPrefix , " " , 10 , " " ) cmd . Flags ( ) . IntVar ( & totalKeys , " " , 1000 , " " ) return cmd }

// NewWatchCommand returns the cobra command for "watcher runner".

NewWatchCommand returns the cobra command for watcher runner .


func NewV3(lg *zap.Logger) Manager {
	if lg == nil {
		lg = zap.NewExample()
	}
	return &v3Manager{lg: lg}
}

func NewV3 ( lg * zap . Logger ) Manager { if lg == nil { lg = zap . NewExample ( ) } return & v3Manager { lg : lg } }

// NewV3 returns a new snapshot Manager for v3.x snapshot.

NewV3 returns a new snapshot Manager for v3 . x snapshot .


func (s *v3Manager) Save(ctx context.Context, cfg clientv3.Config, dbPath string) error {
	if len(cfg.Endpoints) != 1 {
		return fmt.Errorf("snapshot must be requested to one selected node, not multiple %v", cfg.Endpoints)
	}
	cli, err := clientv3.New(cfg)
	if err != nil {
		return err
	}
	defer cli.Close()

	partpath := dbPath + ".part"
	defer os.RemoveAll(partpath)

	var f *os.File
	f, err = os.OpenFile(partpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fileutil.PrivateFileMode)
	if err != nil {
		return fmt.Errorf("could not open %s (%v)", partpath, err)
	}
	s.lg.Info(
		"created temporary db file",
		zap.String("path", partpath),
	)

	now := time.Now()
	var rd io.ReadCloser
	rd, err = cli.Snapshot(ctx)
	if err != nil {
		return err
	}
	s.lg.Info(
		"fetching snapshot",
		zap.String("endpoint", cfg.Endpoints[0]),
	)
	if _, err = io.Copy(f, rd); err != nil {
		return err
	}
	if err = fileutil.Fsync(f); err != nil {
		return err
	}
	if err = f.Close(); err != nil {
		return err
	}
	s.lg.Info(
		"fetched snapshot",
		zap.String("endpoint", cfg.Endpoints[0]),
		zap.Duration("took", time.Since(now)),
	)

	if err = os.Rename(partpath, dbPath); err != nil {
		return fmt.Errorf("could not rename %s to %s (%v)", partpath, dbPath, err)
	}
	s.lg.Info("saved", zap.String("path", dbPath))
	return nil
}

func ( s * v3Manager ) Save ( ctx context . Context , cfg clientv3 . Config , dbPath string ) error { if len ( cfg . Endpoints ) != 1 { return fmt . Errorf ( " " , cfg . Endpoints ) } cli , err := clientv3 . New ( cfg ) if err != nil { return err } defer cli . Close ( ) partpath := dbPath + " " defer os . RemoveAll ( partpath ) var f * os . File f , err = os . OpenFile ( partpath , os . O_WRONLY | os . O_CREATE | os . O_TRUNC , fileutil . PrivateFileMode ) if err != nil { return fmt . Errorf ( " " , partpath , err ) } s . lg . Info ( " " , zap . String ( " " , partpath ) , ) now := time . Now ( ) var rd io . ReadCloser rd , err = cli . Snapshot ( ctx ) if err != nil { return err } s . lg . Info ( " " , zap . String ( " " , cfg . Endpoints [ 0 ] ) , ) if _ , err = io . Copy ( f , rd ) ; err != nil { return err } if err = fileutil . Fsync ( f ) ; err != nil { return err } if err = f . Close ( ) ; err != nil { return err } s . lg . Info ( " " , zap . String ( " " , cfg . Endpoints [ 0 ] ) , zap . Duration ( " " , time . Since ( now ) ) , ) if err = os . Rename ( partpath , dbPath ) ; err != nil { return fmt . Errorf ( " " , partpath , dbPath , err ) } s . lg . Info ( " " , zap . String ( " " , dbPath ) ) return nil }

// Save fetches snapshot from remote etcd server and saves data to target path.

Save fetches snapshot from remote etcd server and saves data to target path .


func (s *v3Manager) Status(dbPath string) (ds Status, err error) {
	if _, err = os.Stat(dbPath); err != nil {
		return ds, err
	}

	db, err := bolt.Open(dbPath, 0400, &bolt.Options{ReadOnly: true})
	if err != nil {
		return ds, err
	}
	defer db.Close()

	h := crc32.New(crc32.MakeTable(crc32.Castagnoli))

	if err = db.View(func(tx *bolt.Tx) error {
		// check snapshot file integrity first
		var dbErrStrings []string
		for dbErr := range tx.Check() {
			dbErrStrings = append(dbErrStrings, dbErr.Error())
		}
		if len(dbErrStrings) > 0 {
			return fmt.Errorf("snapshot file integrity check failed. %d errors found.\n"+strings.Join(dbErrStrings, "\n"), len(dbErrStrings))
		}
		ds.TotalSize = tx.Size()
		c := tx.Cursor()
		for next, _ := c.First(); next != nil; next, _ = c.Next() {
			b := tx.Bucket(next)
			if b == nil {
				return fmt.Errorf("cannot get hash of bucket %s", string(next))
			}
			h.Write(next)
			iskeyb := (string(next) == "key")
			b.ForEach(func(k, v []byte) error {
				h.Write(k)
				h.Write(v)
				if iskeyb {
					rev := bytesToRev(k)
					ds.Revision = rev.main
				}
				ds.TotalKey++
				return nil
			})
		}
		return nil
	}); err != nil {
		return ds, err
	}

	ds.Hash = h.Sum32()
	return ds, nil
}

func
(
s
*
v3Manager
)
Status
(
dbPath
string
)
(
ds
Status
,
err
error
)
{
if
_
,
err
=
os
.
Stat
(
dbPath
)
;
err
!=
{
return
ds
,
err
}
db
,
err
:=
bolt
.
Open
(
dbPath
,
0400
,
&
bolt
.
Options
{
ReadOnly
:
true
}
)
if
err
!=
{
return
ds
,
err
}
defer
db
.
Close
(
)
h
:=
crc32
.
New
(
crc32
.
MakeTable
(
crc32
.
Castagnoli
)
)
if
err
=
db
.
View
(
func
(
tx
*
bolt
.
Tx
)
error
{
// check snapshot file integrity first
var
dbErrStrings
[
]
string
for
dbErr
:=
range
tx
.
Check
(
)
{
dbErrStrings
=
append
(
dbErrStrings
,
dbErr
.
Error
(
)
)
}
if
len
(
dbErrStrings
)
>
0
{
return
fmt
.
Errorf
(
"
\n
"
+
strings
.
Join
(
dbErrStrings
,
"
\n
"
)
,
len
(
dbErrStrings
)
)
}
ds
.
TotalSize
=
tx
.
Size
(
)
c
:=
tx
.
Cursor
(
)
for
next
,
_
:=
c
.
First
(
)
;
next
!=
;
next
,
_
=
c
.
Next
(
)
{
b
:=
tx
.
Bucket
(
next
)
if
b
==
{
return
fmt
.
Errorf
(
"
"
,
string
(
next
)
)
}
h
.
Write
(
next
)
iskeyb
:=
(
string
(
next
)
==
"
"
)
b
.
ForEach
(
func
(
k
,
v
[
]
byte
)
error
{
h
.
Write
(
k
)
h
.
Write
(
v
)
if
iskeyb
{
rev
:=
bytesToRev
(
k
)
ds
.
Revision
=
rev
.
main
}
ds
.
TotalKey
++
return
}
)
}
return
}
)
;
err
!=
{
return
ds
,
err
}
ds
.
Hash
=
h
.
Sum32
(
)
return
ds
,
}

// Status returns the snapshot file information.

Status returns the snapshot file information .


func (s *v3Manager) Restore(cfg RestoreConfig) error {
	pURLs, err := types.NewURLs(cfg.PeerURLs)
	if err != nil {
		return err
	}
	var ics types.URLsMap
	ics, err = types.NewURLsMap(cfg.InitialCluster)
	if err != nil {
		return err
	}

	srv := etcdserver.ServerConfig{
		Logger:              s.lg,
		Name:                cfg.Name,
		PeerURLs:            pURLs,
		InitialPeerURLsMap:  ics,
		InitialClusterToken: cfg.InitialClusterToken,
	}
	if err = srv.VerifyBootstrap(); err != nil {
		return err
	}

	s.cl, err = membership.NewClusterFromURLsMap(s.lg, cfg.InitialClusterToken, ics)
	if err != nil {
		return err
	}

	dataDir := cfg.OutputDataDir
	if dataDir == "" {
		dataDir = cfg.Name + ".etcd"
	}
	if fileutil.Exist(dataDir) {
		return fmt.Errorf("data-dir %q exists", dataDir)
	}

	walDir := cfg.OutputWALDir
	if walDir == "" {
		walDir = filepath.Join(dataDir, "member", "wal")
	} else if fileutil.Exist(walDir) {
		return fmt.Errorf("wal-dir %q exists", walDir)
	}

	s.name = cfg.Name
	s.dbPath = cfg.SnapshotPath
	s.walDir = walDir
	s.snapDir = filepath.Join(dataDir, "member", "snap")
	s.skipHashCheck = cfg.SkipHashCheck

	s.lg.Info(
		"restoring snapshot",
		zap.String("path", s.dbPath),
		zap.String("wal-dir", s.walDir),
		zap.String("data-dir", dataDir),
		zap.String("snap-dir", s.snapDir),
	)
	if err = s.saveDB(); err != nil {
		return err
	}
	if err = s.saveWALAndSnap(); err != nil {
		return err
	}
	s.lg.Info(
		"restored snapshot",
		zap.String("path", s.dbPath),
		zap.String("wal-dir", s.walDir),
		zap.String("data-dir", dataDir),
		zap.String("snap-dir", s.snapDir),
	)

	return nil
}

func ( s * v3Manager ) Restore ( cfg RestoreConfig ) error { pURLs , err := types . NewURLs ( cfg . PeerURLs ) if err != nil { return err } var ics types . URLsMap ics , err = types . NewURLsMap ( cfg . InitialCluster ) if err != nil { return err } srv := etcdserver . ServerConfig { Logger : s . lg , Name : cfg . Name , PeerURLs : pURLs , InitialPeerURLsMap : ics , InitialClusterToken : cfg . InitialClusterToken , } if err = srv . VerifyBootstrap ( ) ; err != nil { return err } s . cl , err = membership . NewClusterFromURLsMap ( s . lg , cfg . InitialClusterToken , ics ) if err != nil { return err } dataDir := cfg . OutputDataDir if dataDir == " " { dataDir = cfg . Name + " " } if fileutil . Exist ( dataDir ) { return fmt . Errorf ( " " , dataDir ) } walDir := cfg . OutputWALDir if walDir == " " { walDir = filepath . Join ( dataDir , " " , " " ) } else if fileutil . Exist ( walDir ) { return fmt . Errorf ( " " , walDir ) } s . name = cfg . Name s . dbPath = cfg . SnapshotPath s . walDir = walDir s . snapDir = filepath . Join ( dataDir , " " , " " ) s . skipHashCheck = cfg . SkipHashCheck s . lg . Info ( " " , zap . String ( " " , s . dbPath ) , zap . String ( " " , s . walDir ) , zap . String ( " " , dataDir ) , zap . String ( " " , s . snapDir ) , ) if err = s . saveDB ( ) ; err != nil { return err } if err = s . saveWALAndSnap ( ) ; err != nil { return err } s . lg . Info ( " " , zap . String ( " " , s . dbPath ) , zap . String ( " " , s . walDir ) , zap . String ( " " , dataDir ) , zap . String ( " " , s . snapDir ) , ) return nil }

// Restore restores a new etcd data directory from given snapshot file.

Restore restores a new etcd data directory from given snapshot file .


func NewAuthStore(lg *zap.Logger, be backend.Backend, tp TokenProvider, bcryptCost int) *authStore {
	if bcryptCost < bcrypt.MinCost || bcryptCost > bcrypt.MaxCost {
		if lg != nil {
			lg.Warn(
				"use default bcrypt cost instead of the invalid given cost",
				zap.Int("min-cost", bcrypt.MinCost),
				zap.Int("max-cost", bcrypt.MaxCost),
				zap.Int("default-cost", bcrypt.DefaultCost),
				zap.Int("given-cost", bcryptCost))
		} else {
			plog.Warningf("Use default bcrypt-cost %d instead of the invalid value %d",
				bcrypt.DefaultCost, bcryptCost)
		}

		bcryptCost = bcrypt.DefaultCost
	}

	tx := be.BatchTx()
	tx.Lock()

	tx.UnsafeCreateBucket(authBucketName)
	tx.UnsafeCreateBucket(authUsersBucketName)
	tx.UnsafeCreateBucket(authRolesBucketName)

	enabled := false
	_, vs := tx.UnsafeRange(authBucketName, enableFlagKey, nil, 0)
	if len(vs) == 1 {
		if bytes.Equal(vs[0], authEnabled) {
			enabled = true
		}
	}

	as := &authStore{
		revision:       getRevision(tx),
		lg:             lg,
		be:             be,
		enabled:        enabled,
		rangePermCache: make(map[string]*unifiedRangePermissions),
		tokenProvider:  tp,
		bcryptCost:     bcryptCost,
	}

	if enabled {
		as.tokenProvider.enable()
	}

	if as.Revision() == 0 {
		as.commitRevision(tx)
	}

	tx.Unlock()
	be.ForceCommit()

	return as
}

func NewAuthStore ( lg * zap . Logger , be backend . Backend , tp TokenProvider , bcryptCost int ) * authStore { if bcryptCost < bcrypt . MinCost || bcryptCost > bcrypt . MaxCost { if lg != nil { lg . Warn ( " " , zap . Int ( " " , bcrypt . MinCost ) , zap . Int ( " " , bcrypt . MaxCost ) , zap . Int ( " " , bcrypt . DefaultCost ) , zap . Int ( " " , bcryptCost ) ) } else { plog . Warningf ( " " , bcrypt . DefaultCost , bcryptCost ) } bcryptCost = bcrypt . DefaultCost } tx := be . BatchTx ( ) tx . Lock ( ) tx . UnsafeCreateBucket ( authBucketName ) tx . UnsafeCreateBucket ( authUsersBucketName ) tx . UnsafeCreateBucket ( authRolesBucketName ) enabled := false _ , vs := tx . UnsafeRange ( authBucketName , enableFlagKey , nil , 0 ) if len ( vs ) == 1 { if bytes . Equal ( vs [ 0 ] , authEnabled ) { enabled = true } } as := & authStore { revision : getRevision ( tx ) , lg : lg , be : be , enabled : enabled , rangePermCache : make ( map [ string ] * unifiedRangePermissions ) , tokenProvider : tp , bcryptCost : bcryptCost , } if enabled { as . tokenProvider . enable ( ) } if as . Revision ( ) == 0 { as . commitRevision ( tx ) } tx . Unlock ( ) be . ForceCommit ( ) return as }

// NewAuthStore creates a new AuthStore.


func NewTokenProvider(
	lg *zap.Logger,
	tokenOpts string,
	indexWaiter func(uint64) <-chan struct{}) (TokenProvider, error) {
	tokenType, typeSpecificOpts, err := decomposeOpts(lg, tokenOpts)
	if err != nil {
		return nil, ErrInvalidAuthOpts
	}

	switch tokenType {
	case tokenTypeSimple:
		if lg != nil {
			lg.Warn("simple token is not cryptographically signed")
		} else {
			plog.Warningf("simple token is not cryptographically signed")
		}
		return newTokenProviderSimple(lg, indexWaiter), nil

	case tokenTypeJWT:
		return newTokenProviderJWT(lg, typeSpecificOpts)

	case "":
		return newTokenProviderNop()

	default:
		if lg != nil {
			lg.Warn(
				"unknown token type",
				zap.String("type", tokenType),
				zap.Error(ErrInvalidAuthOpts),
			)
		} else {
			plog.Errorf("unknown token type: %s", tokenType)
		}
		return nil, ErrInvalidAuthOpts
	}
}

func NewTokenProvider ( lg * zap . Logger , tokenOpts string , indexWaiter func ( uint64 ) <- chan struct { } ) ( TokenProvider , error ) { tokenType , typeSpecificOpts , err := decomposeOpts ( lg , tokenOpts ) if err != nil { return nil , ErrInvalidAuthOpts } switch tokenType { case tokenTypeSimple : if lg != nil { lg . Warn ( " " ) } else { plog . Warningf ( " " ) } return newTokenProviderSimple ( lg , indexWaiter ) , nil case tokenTypeJWT : return newTokenProviderJWT ( lg , typeSpecificOpts ) case " " : return newTokenProviderNop ( ) default : if lg != nil { lg . Warn ( " " , zap . String ( " " , tokenType ) , zap . Error ( ErrInvalidAuthOpts ) , ) } else { plog . Errorf ( " " , tokenType ) } return nil , ErrInvalidAuthOpts } }

// NewTokenProvider creates a new token provider.

NewTokenProvider creates a new token provider .


func (t *Transport) MendPeer(id types.ID) {
	t.mu.RLock()
	p, pok := t.peers[id]
	g, gok := t.remotes[id]
	t.mu.RUnlock()

	if pok {
		p.(Pausable).Resume()
	}
	if gok {
		g.Resume()
	}
}

func ( t * Transport ) MendPeer ( id types . ID ) { t . mu . RLock ( ) p , pok := t . peers [ id ] g , gok := t . remotes [ id ] t . mu . RUnlock ( ) if pok { p . ( Pausable ) . Resume ( ) } if gok { g . Resume ( ) } }

// MendPeer recovers the message dropping behavior of the given peer.

MendPeer recovers the message dropping behavior of the given peer .


func (t *Transport) removePeer(id types.ID) {
	if peer, ok := t.peers[id]; ok {
		peer.stop()
	} else {
		if t.Logger != nil {
			t.Logger.Panic("unexpected removal of unknown remote peer", zap.String("remote-peer-id", id.String()))
		} else {
			plog.Panicf("unexpected removal of unknown peer '%d'", id)
		}
	}
	delete(t.peers, id)
	delete(t.LeaderStats.Followers, id.String())
	t.pipelineProber.Remove(id.String())
	t.streamProber.Remove(id.String())

	if t.Logger != nil {
		t.Logger.Info(
			"removed remote peer",
			zap.String("local-member-id", t.ID.String()),
			zap.String("removed-remote-peer-id", id.String()),
		)
	} else {
		plog.Infof("removed peer %s", id)
	}
}

func ( t * Transport ) removePeer ( id types . ID ) { if peer , ok := t . peers [ id ] ; ok { peer . stop ( ) } else { if t . Logger != nil { t . Logger . Panic ( " " , zap . String ( " " , id . String ( ) ) ) } else { plog . Panicf ( " " , id ) } } delete ( t . peers , id ) delete ( t . LeaderStats . Followers , id . String ( ) ) t . pipelineProber . Remove ( id . String ( ) ) t . streamProber . Remove ( id . String ( ) ) if t . Logger != nil { t . Logger . Info ( " " , zap . String ( " " , t . ID . String ( ) ) , zap . String ( " " , id . String ( ) ) , ) } else { plog . Infof ( " " , id ) } }

// the caller of this function must have the peers mutex.

the caller of this function must have the peers mutex .


func (t *Transport) ActivePeers() (cnt int) {
	t.mu.RLock()
	defer t.mu.RUnlock()
	for _, p := range t.peers {
		if !p.activeSince().IsZero() {
			cnt++
		}
	}
	return cnt
}

func ( t * Transport ) ActivePeers ( ) ( cnt int ) { t . mu . RLock ( ) defer t . mu . RUnlock ( ) for _ , p := range t . peers { if ! p . activeSince ( ) . IsZero ( ) { cnt ++ } } return cnt }

// ActivePeers returns a channel that closes when an initial
// peer connection has been established. Use this to wait until the
// first peer connection becomes active.

ActivePeers returns a channel that closes when an initial peer connection has been established . Use this to wait until the first peer connection becomes active .


func resolveTCPAddrDefault(ctx context.Context, addr string) (*net.TCPAddr, error) {
	host, port, serr := net.SplitHostPort(addr)
	if serr != nil {
		return nil, serr
	}
	portnum, perr := net.DefaultResolver.LookupPort(ctx, "tcp", port)
	if perr != nil {
		return nil, perr
	}

	var ips []net.IPAddr
	if ip := net.ParseIP(host); ip != nil {
		ips = []net.IPAddr{{IP: ip}}
	} else {
		// Try as a DNS name.
		ipss, err := net.DefaultResolver.LookupIPAddr(ctx, host)
		if err != nil {
			return nil, err
		}
		ips = ipss
	}
	// randomize?
	ip := ips[0]
	return &net.TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}, nil
}

func
resolveTCPAddrDefault
(
ctx
context
.
Context
,
addr
string
)
(
*
net
.
TCPAddr
,
error
)
{
host
,
port
,
serr
:=
net
.
SplitHostPort
(
addr
)
if
serr
!=
{
return
,
serr
}
portnum
,
perr
:=
net
.
DefaultResolver
.
LookupPort
(
ctx
,
"
"
,
port
)
if
perr
!=
{
return
,
perr
}
var
ips
[
]
net
.
IPAddr
if
ip
:=
net
.
ParseIP
(
host
)
;
ip
!=
{
ips
=
[
]
net
.
IPAddr
{
{
IP
:
ip
}
}
}
else
{
// Try as a DNS name.
ipss
,
err
:=
net
.
DefaultResolver
.
LookupIPAddr
(
ctx
,
host
)
if
err
!=
{
return
,
err
}
ips
=
ipss
}
// randomize?
ip
:=
ips
[
0
]
return
&
net
.
TCPAddr
{
IP
:
ip
.
IP
,
Port
:
portnum
,
Zone
:
ip
.
Zone
}
,
}

// taken from go's ResolveTCP code but uses configurable ctx

taken from go s ResolveTCP code but uses configurable ctx


func resolveTCPAddrs(ctx context.Context, lg *zap.Logger, urls [][]url.URL) ([][]url.URL, error) {
	newurls := make([][]url.URL, 0)
	for _, us := range urls {
		nus := make([]url.URL, len(us))
		for i, u := range us {
			nu, err := url.Parse(u.String())
			if err != nil {
				return nil, fmt.Errorf("failed to parse %q (%v)", u.String(), err)
			}
			nus[i] = *nu
		}
		for i, u := range nus {
			h, err := resolveURL(ctx, lg, u)
			if err != nil {
				return nil, fmt.Errorf("failed to resolve %q (%v)", u.String(), err)
			}
			if h != "" {
				nus[i].Host = h
			}
		}
		newurls = append(newurls, nus)
	}
	return newurls, nil
}

func resolveTCPAddrs ( ctx context . Context , lg * zap . Logger , urls [ ] [ ] url . URL ) ( [ ] [ ] url . URL , error ) { newurls := make ( [ ] [ ] url . URL , 0 ) for _ , us := range urls { nus := make ( [ ] url . URL , len ( us ) ) for i , u := range us { nu , err := url . Parse ( u . String ( ) ) if err != nil { return nil , fmt . Errorf ( " " , u . String ( ) , err ) } nus [ i ] = * nu } for i , u := range nus { h , err := resolveURL ( ctx , lg , u ) if err != nil { return nil , fmt . Errorf ( " " , u . String ( ) , err ) } if h != " " { nus [ i ] . Host = h } } newurls = append ( newurls , nus ) } return newurls , nil }

// resolveTCPAddrs is a convenience wrapper for net.ResolveTCPAddr.
// resolveTCPAddrs return a new set of url.URLs, in which all DNS hostnames
// are resolved.

resolveTCPAddrs is a convenience wrapper for net . ResolveTCPAddr . resolveTCPAddrs return a new set of url . URLs in which all DNS hostnames are resolved .


func urlsEqual(ctx context.Context, lg *zap.Logger, a []url.URL, b []url.URL) (bool, error) {
	if len(a) != len(b) {
		return false, fmt.Errorf("len(%q) != len(%q)", urlsToStrings(a), urlsToStrings(b))
	}
	urls, err := resolveTCPAddrs(ctx, lg, [][]url.URL{a, b})
	if err != nil {
		return false, err
	}
	preva, prevb := a, b
	a, b = urls[0], urls[1]
	sort.Sort(types.URLs(a))
	sort.Sort(types.URLs(b))
	for i := range a {
		if !reflect.DeepEqual(a[i], b[i]) {
			return false, fmt.Errorf("%q(resolved from %q) != %q(resolved from %q)",
				a[i].String(), preva[i].String(),
				b[i].String(), prevb[i].String(),
			)
		}
	}
	return true, nil
}

func urlsEqual ( ctx context . Context , lg * zap . Logger , a [ ] url . URL , b [ ] url . URL ) ( bool , error ) { if len ( a ) != len ( b ) { return false , fmt . Errorf ( " " , urlsToStrings ( a ) , urlsToStrings ( b ) ) } urls , err := resolveTCPAddrs ( ctx , lg , [ ] [ ] url . URL { a , b } ) if err != nil { return false , err } preva , prevb := a , b a , b = urls [ 0 ] , urls [ 1 ] sort . Sort ( types . URLs ( a ) ) sort . Sort ( types . URLs ( b ) ) for i := range a { if ! reflect . DeepEqual ( a [ i ] , b [ i ] ) { return false , fmt . Errorf ( " " , a [ i ] . String ( ) , preva [ i ] . String ( ) , b [ i ] . String ( ) , prevb [ i ] . String ( ) , ) } } return true , nil }

// urlsEqual checks equality of url.URLS between two arrays.
// This check pass even if an URL is in hostname and opposite is in IP address.

urlsEqual checks equality of url . URLS between two arrays . This check pass even if an URL is in hostname and opposite is in IP address .


func URLStringsEqual(ctx context.Context, lg *zap.Logger, a []string, b []string) (bool, error) {
	if len(a) != len(b) {
		return false, fmt.Errorf("len(%q) != len(%q)", a, b)
	}
	urlsA := make([]url.URL, 0)
	for _, str := range a {
		u, err := url.Parse(str)
		if err != nil {
			return false, fmt.Errorf("failed to parse %q", str)
		}
		urlsA = append(urlsA, *u)
	}
	urlsB := make([]url.URL, 0)
	for _, str := range b {
		u, err := url.Parse(str)
		if err != nil {
			return false, fmt.Errorf("failed to parse %q", str)
		}
		urlsB = append(urlsB, *u)
	}
	if lg == nil {
		lg, _ = zap.NewProduction()
		if lg == nil {
			lg = zap.NewExample()
		}
	}
	return urlsEqual(ctx, lg, urlsA, urlsB)
}

func URLStringsEqual ( ctx context . Context , lg * zap . Logger , a [ ] string , b [ ] string ) ( bool , error ) { if len ( a ) != len ( b ) { return false , fmt . Errorf ( " " , a , b ) } urlsA := make ( [ ] url . URL , 0 ) for _ , str := range a { u , err := url . Parse ( str ) if err != nil { return false , fmt . Errorf ( " " , str ) } urlsA = append ( urlsA , * u ) } urlsB := make ( [ ] url . URL , 0 ) for _ , str := range b { u , err := url . Parse ( str ) if err != nil { return false , fmt . Errorf ( " " , str ) } urlsB = append ( urlsB , * u ) } if lg == nil { lg , _ = zap . NewProduction ( ) if lg == nil { lg = zap . NewExample ( ) } } return urlsEqual ( ctx , lg , urlsA , urlsB ) }

// URLStringsEqual returns "true" if given URLs are valid
// and resolved to same IP addresses. Otherwise, return "false"
// and error, if any.

URLStringsEqual returns true if given URLs are valid and resolved to same IP addresses . Otherwise return false and error if any .


func NewLeaseCommand() *cobra.Command {
	lc := &cobra.Command{
		Use:   "lease <subcommand>",
		Short: "Lease related commands",
	}

	lc.AddCommand(NewLeaseGrantCommand())
	lc.AddCommand(NewLeaseRevokeCommand())
	lc.AddCommand(NewLeaseTimeToLiveCommand())
	lc.AddCommand(NewLeaseListCommand())
	lc.AddCommand(NewLeaseKeepAliveCommand())

	return lc
}

func NewLeaseCommand ( ) * cobra . Command { lc := & cobra . Command { Use : " " , Short : " " , } lc . AddCommand ( NewLeaseGrantCommand ( ) ) lc . AddCommand ( NewLeaseRevokeCommand ( ) ) lc . AddCommand ( NewLeaseTimeToLiveCommand ( ) ) lc . AddCommand ( NewLeaseListCommand ( ) ) lc . AddCommand ( NewLeaseKeepAliveCommand ( ) ) return lc }

// NewLeaseCommand returns the cobra command for "lease".

NewLeaseCommand returns the cobra command for lease .


func NewLeaseGrantCommand() *cobra.Command {
	lc := &cobra.Command{
		Use:   "grant <ttl>",
		Short: "Creates leases",

		Run: leaseGrantCommandFunc,
	}

	return lc
}

func NewLeaseGrantCommand ( ) * cobra . Command { lc := & cobra . Command { Use : " " , Short : " " , Run : leaseGrantCommandFunc , } return lc }

// NewLeaseGrantCommand returns the cobra command for "lease grant".

NewLeaseGrantCommand returns the cobra command for lease grant .


func leaseGrantCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("lease grant command needs TTL argument"))
	}

	ttl, err := strconv.ParseInt(args[0], 10, 64)
	if err != nil {
		ExitWithError(ExitBadArgs, fmt.Errorf("bad TTL (%v)", err))
	}

	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).Grant(ctx, ttl)
	cancel()
	if err != nil {
		ExitWithError(ExitError, fmt.Errorf("failed to grant lease (%v)", err))
	}
	display.Grant(*resp)
}

func leaseGrantCommandFunc ( cmd * cobra . Command , args [ ] string ) { if len ( args ) != 1 { ExitWithError ( ExitBadArgs , fmt . Errorf ( " " ) ) } ttl , err := strconv . ParseInt ( args [ 0 ] , 10 , 64 ) if err != nil { ExitWithError ( ExitBadArgs , fmt . Errorf ( " " , err ) ) } ctx , cancel := commandCtx ( cmd ) resp , err := mustClientFromCmd ( cmd ) . Grant ( ctx , ttl ) cancel ( ) if err != nil { ExitWithError ( ExitError , fmt . Errorf ( " " , err ) ) } display . Grant ( * resp ) }

// leaseGrantCommandFunc executes the "lease grant" command.

leaseGrantCommandFunc executes the lease grant command .


func NewLeaseRevokeCommand() *cobra.Command {
	lc := &cobra.Command{
		Use:   "revoke <leaseID>",
		Short: "Revokes leases",

		Run: leaseRevokeCommandFunc,
	}

	return lc
}

func NewLeaseRevokeCommand ( ) * cobra . Command { lc := & cobra . Command { Use : " " , Short : " " , Run : leaseRevokeCommandFunc , } return lc }

// NewLeaseRevokeCommand returns the cobra command for "lease revoke".

NewLeaseRevokeCommand returns the cobra command for lease revoke .


func leaseRevokeCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("lease revoke command needs 1 argument"))
	}

	id := leaseFromArgs(args[0])
	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).Revoke(ctx, id)
	cancel()
	if err != nil {
		ExitWithError(ExitError, fmt.Errorf("failed to revoke lease (%v)", err))
	}
	display.Revoke(id, *resp)
}

func leaseRevokeCommandFunc ( cmd * cobra . Command , args [ ] string ) { if len ( args ) != 1 { ExitWithError ( ExitBadArgs , fmt . Errorf ( " " ) ) } id := leaseFromArgs ( args [ 0 ] ) ctx , cancel := commandCtx ( cmd ) resp , err := mustClientFromCmd ( cmd ) . Revoke ( ctx , id ) cancel ( ) if err != nil { ExitWithError ( ExitError , fmt . Errorf ( " " , err ) ) } display . Revoke ( id , * resp ) }

// leaseRevokeCommandFunc executes the "lease grant" command.

leaseRevokeCommandFunc executes the lease grant command .


func NewLeaseTimeToLiveCommand() *cobra.Command {
	lc := &cobra.Command{
		Use:   "timetolive <leaseID> [options]",
		Short: "Get lease information",

		Run: leaseTimeToLiveCommandFunc,
	}
	lc.Flags().BoolVar(&timeToLiveKeys, "keys", false, "Get keys attached to this lease")

	return lc
}

func NewLeaseTimeToLiveCommand ( ) * cobra . Command { lc := & cobra . Command { Use : " " , Short : " " , Run : leaseTimeToLiveCommandFunc , } lc . Flags ( ) . BoolVar ( & timeToLiveKeys , " " , false , " " ) return lc }

// NewLeaseTimeToLiveCommand returns the cobra command for "lease timetolive".

NewLeaseTimeToLiveCommand returns the cobra command for lease timetolive .


func leaseTimeToLiveCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("lease timetolive command needs lease ID as argument"))
	}
	var opts []v3.LeaseOption
	if timeToLiveKeys {
		opts = append(opts, v3.WithAttachedKeys())
	}
	resp, rerr := mustClientFromCmd(cmd).TimeToLive(context.TODO(), leaseFromArgs(args[0]), opts...)
	if rerr != nil {
		ExitWithError(ExitBadConnection, rerr)
	}
	display.TimeToLive(*resp, timeToLiveKeys)
}

func leaseTimeToLiveCommandFunc ( cmd * cobra . Command , args [ ] string ) { if len ( args ) != 1 { ExitWithError ( ExitBadArgs , fmt . Errorf ( " " ) ) } var opts [ ] v3 . LeaseOption if timeToLiveKeys { opts = append ( opts , v3 . WithAttachedKeys ( ) ) } resp , rerr := mustClientFromCmd ( cmd ) . TimeToLive ( context . TODO ( ) , leaseFromArgs ( args [ 0 ] ) , opts ... ) if rerr != nil { ExitWithError ( ExitBadConnection , rerr ) } display . TimeToLive ( * resp , timeToLiveKeys ) }

// leaseTimeToLiveCommandFunc executes the "lease timetolive" command.

leaseTimeToLiveCommandFunc executes the lease timetolive command .


func NewLeaseListCommand() *cobra.Command {
	lc := &cobra.Command{
		Use:   "list",
		Short: "List all active leases",
		Run:   leaseListCommandFunc,
	}
	return lc
}

func NewLeaseListCommand ( ) * cobra . Command { lc := & cobra . Command { Use : " " , Short : " " , Run : leaseListCommandFunc , } return lc }

// NewLeaseListCommand returns the cobra command for "lease list".

NewLeaseListCommand returns the cobra command for lease list .


func leaseListCommandFunc(cmd *cobra.Command, args []string) {
	resp, rerr := mustClientFromCmd(cmd).Leases(context.TODO())
	if rerr != nil {
		ExitWithError(ExitBadConnection, rerr)
	}
	display.Leases(*resp)
}

func leaseListCommandFunc ( cmd * cobra . Command , args [ ] string ) { resp , rerr := mustClientFromCmd ( cmd ) . Leases ( context . TODO ( ) ) if rerr != nil { ExitWithError ( ExitBadConnection , rerr ) } display . Leases ( * resp ) }

// leaseListCommandFunc executes the "lease list" command.

leaseListCommandFunc executes the lease list command .


func NewLeaseKeepAliveCommand() *cobra.Command {
	lc := &cobra.Command{
		Use:   "keep-alive [options] <leaseID>",
		Short: "Keeps leases alive (renew)",

		Run: leaseKeepAliveCommandFunc,
	}

	lc.Flags().BoolVar(&leaseKeepAliveOnce, "once", false, "Resets the keep-alive time to its original value and exits immediately")

	return lc
}

func NewLeaseKeepAliveCommand ( ) * cobra . Command { lc := & cobra . Command { Use : " " , Short : " " , Run : leaseKeepAliveCommandFunc , } lc . Flags ( ) . BoolVar ( & leaseKeepAliveOnce , " " , false , " " ) return lc }

// NewLeaseKeepAliveCommand returns the cobra command for "lease keep-alive".

NewLeaseKeepAliveCommand returns the cobra command for lease keep - alive .


func leaseKeepAliveCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		ExitWithError(ExitBadArgs, fmt.Errorf("lease keep-alive command needs lease ID as argument"))
	}

	id := leaseFromArgs(args[0])

	if leaseKeepAliveOnce {
		respc, kerr := mustClientFromCmd(cmd).KeepAliveOnce(context.TODO(), id)
		if kerr != nil {
			ExitWithError(ExitBadConnection, kerr)
		}
		display.KeepAlive(*respc)
		return
	}

	respc, kerr := mustClientFromCmd(cmd).KeepAlive(context.TODO(), id)
	if kerr != nil {
		ExitWithError(ExitBadConnection, kerr)
	}
	for resp := range respc {
		display.KeepAlive(*resp)
	}

	if _, ok := (display).(*simplePrinter); ok {
		fmt.Printf("lease %016x expired or revoked.\n", id)
	}
}

func leaseKeepAliveCommandFunc ( cmd * cobra . Command , args [ ] string ) { if len ( args ) != 1 { ExitWithError ( ExitBadArgs , fmt . Errorf ( " " ) ) } id := leaseFromArgs ( args [ 0 ] ) if leaseKeepAliveOnce { respc , kerr := mustClientFromCmd ( cmd ) . KeepAliveOnce ( context . TODO ( ) , id ) if kerr != nil { ExitWithError ( ExitBadConnection , kerr ) } display . KeepAlive ( * respc ) return } respc , kerr := mustClientFromCmd ( cmd ) . KeepAlive ( context . TODO ( ) , id ) if kerr != nil { ExitWithError ( ExitBadConnection , kerr ) } for resp := range respc { display . KeepAlive ( * resp ) } if _ , ok := ( display ) . ( * simplePrinter ) ; ok { fmt . Printf ( " \n " , id ) } }

// leaseKeepAliveCommandFunc executes the "lease keep-alive" command.

leaseKeepAliveCommandFunc executes the lease keep - alive command .


func NewAlarmCommand() *cobra.Command {
	ac := &cobra.Command{
		Use:   "alarm <subcommand>",
		Short: "Alarm related commands",
	}

	ac.AddCommand(NewAlarmDisarmCommand())
	ac.AddCommand(NewAlarmListCommand())

	return ac
}

func NewAlarmCommand ( ) * cobra . Command { ac := & cobra . Command { Use : " " , Short : " " , } ac . AddCommand ( NewAlarmDisarmCommand ( ) ) ac . AddCommand ( NewAlarmListCommand ( ) ) return ac }

// NewAlarmCommand returns the cobra command for "alarm".

NewAlarmCommand returns the cobra command for alarm .


func alarmDisarmCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 0 {
		ExitWithError(ExitBadArgs, fmt.Errorf("alarm disarm command accepts no arguments"))
	}
	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).AlarmDisarm(ctx, &v3.AlarmMember{})
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}
	display.Alarm(*resp)
}

func alarmDisarmCommandFunc ( cmd * cobra . Command , args [ ] string ) { if len ( args ) != 0 { ExitWithError ( ExitBadArgs , fmt . Errorf ( " " ) ) } ctx , cancel := commandCtx ( cmd ) resp , err := mustClientFromCmd ( cmd ) . AlarmDisarm ( ctx , & v3 . AlarmMember { } ) cancel ( ) if err != nil { ExitWithError ( ExitError , err ) } display . Alarm ( * resp ) }

// alarmDisarmCommandFunc executes the "alarm disarm" command.

alarmDisarmCommandFunc executes the alarm disarm command .


func alarmListCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 0 {
		ExitWithError(ExitBadArgs, fmt.Errorf("alarm list command accepts no arguments"))
	}
	ctx, cancel := commandCtx(cmd)
	resp, err := mustClientFromCmd(cmd).AlarmList(ctx)
	cancel()
	if err != nil {
		ExitWithError(ExitError, err)
	}
	display.Alarm(*resp)
}

func alarmListCommandFunc ( cmd * cobra . Command , args [ ] string ) { if len ( args ) != 0 { ExitWithError ( ExitBadArgs , fmt . Errorf ( " " ) ) } ctx , cancel := commandCtx ( cmd ) resp , err := mustClientFromCmd ( cmd ) . AlarmList ( ctx ) cancel ( ) if err != nil { ExitWithError ( ExitError , err ) } display . Alarm ( * resp ) }

// alarmListCommandFunc executes the "alarm list" command.

alarmListCommandFunc executes the alarm list command .


func (e *Etcd) Flags() (fs []string) {
	tp := reflect.TypeOf(*e)
	vo := reflect.ValueOf(*e)
	for _, name := range etcdFields {
		field, ok := tp.FieldByName(name)
		if !ok {
			panic(fmt.Errorf("field %q not found", name))
		}
		fv := reflect.Indirect(vo).FieldByName(name)
		var sv string
		switch fv.Type().Kind() {
		case reflect.String:
			sv = fv.String()
		case reflect.Slice:
			n := fv.Len()
			sl := make([]string, n)
			for i := 0; i < n; i++ {
				sl[i] = fv.Index(i).String()
			}
			sv = strings.Join(sl, ",")
		case reflect.Int64:
			sv = fmt.Sprintf("%d", fv.Int())
		case reflect.Bool:
			sv = fmt.Sprintf("%v", fv.Bool())
		default:
			panic(fmt.Errorf("field %q (%v) cannot be parsed", name, fv.Type().Kind()))
		}

		fname := field.Tag.Get("yaml")

		// TODO: remove this
		if fname == "initial-corrupt-check" {
			fname = "experimental-" + fname
		}

		if sv != "" {
			fs = append(fs, fmt.Sprintf("--%s=%s", fname, sv))
		}
	}
	return fs
}

func
(
e
*
Etcd
)
Flags
(
)
(
fs
[
]
string
)
{
tp
:=
reflect
.
TypeOf
(
*
e
)
vo
:=
reflect
.
ValueOf
(
*
e
)
for
_
,
name
:=
range
etcdFields
{
field
,
ok
:=
tp
.
FieldByName
(
name
)
if
!
ok
{
panic
(
fmt
.
Errorf
(
"
"
,
name
)
)
}
fv
:=
reflect
.
Indirect
(
vo
)
.
FieldByName
(
name
)
var
sv
string
switch
fv
.
Type
(
)
.
Kind
(
)
{
case
reflect
.
String
:
sv
=
fv
.
String
(
)
case
reflect
.
Slice
:
n
:=
fv
.
Len
(
)
sl
:=
make
(
[
]
string
,
n
)
for
i
:=
0
;
i
<
n
;
i
++
{
sl
[
i
]
=
fv
.
Index
(
i
)
.
String
(
)
}
sv
=
strings
.
Join
(
sl
,
"
"
)
case
reflect
.
Int64
:
sv
=
fmt
.
Sprintf
(
"
"
,
fv
.
Int
(
)
)
case
reflect
.
Bool
:
sv
=
fmt
.
Sprintf
(
"
"
,
fv
.
Bool
(
)
)
default
:
panic
(
fmt
.
Errorf
(
"
"
,
name
,
fv
.
Type
(
)
.
Kind
(
)
)
)
}
fname
:=
field
.
Tag
.
Get
(
"
"
)
// TODO: remove this
if
fname
==
"
"
{
fname
=
"
"
+
fname
}
if
sv
!=
"
"
{
fs
=
append
(
fs
,
fmt
.
Sprintf
(
"
"
,
fname
,
sv
)
)
}
}
return
fs
}

// Flags returns etcd flags in string slice.

Flags returns etcd flags in string slice .


func (e *Etcd) EmbedConfig() (cfg *embed.Config, err error) {
	var lcURLs types.URLs
	lcURLs, err = types.NewURLs(e.ListenClientURLs)
	if err != nil {
		return nil, err
	}
	var acURLs types.URLs
	acURLs, err = types.NewURLs(e.AdvertiseClientURLs)
	if err != nil {
		return nil, err
	}
	var lpURLs types.URLs
	lpURLs, err = types.NewURLs(e.ListenPeerURLs)
	if err != nil {
		return nil, err
	}
	var apURLs types.URLs
	apURLs, err = types.NewURLs(e.AdvertisePeerURLs)
	if err != nil {
		return nil, err
	}

	cfg = embed.NewConfig()
	cfg.Name = e.Name
	cfg.Dir = e.DataDir
	cfg.WalDir = e.WALDir
	cfg.TickMs = uint(e.HeartbeatIntervalMs)
	cfg.ElectionMs = uint(e.ElectionTimeoutMs)

	cfg.LCUrls = lcURLs
	cfg.ACUrls = acURLs
	cfg.ClientAutoTLS = e.ClientAutoTLS
	cfg.ClientTLSInfo = transport.TLSInfo{
		ClientCertAuth: e.ClientCertAuth,
		CertFile:       e.ClientCertFile,
		KeyFile:        e.ClientKeyFile,
		TrustedCAFile:  e.ClientTrustedCAFile,
	}

	cfg.LPUrls = lpURLs
	cfg.APUrls = apURLs
	cfg.PeerAutoTLS = e.PeerAutoTLS
	cfg.PeerTLSInfo = transport.TLSInfo{
		ClientCertAuth: e.PeerClientCertAuth,
		CertFile:       e.PeerCertFile,
		KeyFile:        e.PeerKeyFile,
		TrustedCAFile:  e.PeerTrustedCAFile,
	}

	cfg.InitialCluster = e.InitialCluster
	cfg.ClusterState = e.InitialClusterState
	cfg.InitialClusterToken = e.InitialClusterToken

	cfg.SnapshotCount = uint64(e.SnapshotCount)
	cfg.QuotaBackendBytes = e.QuotaBackendBytes

	cfg.PreVote = e.PreVote
	cfg.ExperimentalInitialCorruptCheck = e.InitialCorruptCheck

	cfg.Logger = e.Logger
	cfg.LogOutputs = e.LogOutputs
	cfg.Debug = e.Debug

	return cfg, nil
}

func ( e * Etcd ) EmbedConfig ( ) ( cfg * embed . Config , err error ) { var lcURLs types . URLs lcURLs , err = types . NewURLs ( e . ListenClientURLs ) if err != nil { return nil , err } var acURLs types . URLs acURLs , err = types . NewURLs ( e . AdvertiseClientURLs ) if err != nil { return nil , err } var lpURLs types . URLs lpURLs , err = types . NewURLs ( e . ListenPeerURLs ) if err != nil { return nil , err } var apURLs types . URLs apURLs , err = types . NewURLs ( e . AdvertisePeerURLs ) if err != nil { return nil , err } cfg = embed . NewConfig ( ) cfg . Name = e . Name cfg . Dir = e . DataDir cfg . WalDir = e . WALDir cfg . TickMs = uint ( e . HeartbeatIntervalMs ) cfg . ElectionMs = uint ( e . ElectionTimeoutMs ) cfg . LCUrls = lcURLs cfg . ACUrls = acURLs cfg . ClientAutoTLS = e . ClientAutoTLS cfg . ClientTLSInfo = transport . TLSInfo { ClientCertAuth : e . ClientCertAuth , CertFile : e . ClientCertFile , KeyFile : e . ClientKeyFile , TrustedCAFile : e . ClientTrustedCAFile , } cfg . LPUrls = lpURLs cfg . APUrls = apURLs cfg . PeerAutoTLS = e . PeerAutoTLS cfg . PeerTLSInfo = transport . TLSInfo { ClientCertAuth : e . PeerClientCertAuth , CertFile : e . PeerCertFile , KeyFile : e . PeerKeyFile , TrustedCAFile : e . PeerTrustedCAFile , } cfg . InitialCluster = e . InitialCluster cfg . ClusterState = e . InitialClusterState cfg . InitialClusterToken = e . InitialClusterToken cfg . SnapshotCount = uint64 ( e . SnapshotCount ) cfg . QuotaBackendBytes = e . QuotaBackendBytes cfg . PreVote = e . PreVote cfg . ExperimentalInitialCorruptCheck = e . InitialCorruptCheck cfg . Logger = e . Logger cfg . LogOutputs = e . LogOutputs cfg . Debug = e . Debug return cfg , nil }

// EmbedConfig returns etcd embed.Config.

EmbedConfig returns etcd embed . Config .


func PProfHandlers() map[string]http.Handler {
	// set only when there's no existing setting
	if runtime.SetMutexProfileFraction(-1) == 0 {
		// 1 out of 5 mutex events are reported, on average
		runtime.SetMutexProfileFraction(5)
	}

	m := make(map[string]http.Handler)

	m[HTTPPrefixPProf+"/"] = http.HandlerFunc(pprof.Index)
	m[HTTPPrefixPProf+"/profile"] = http.HandlerFunc(pprof.Profile)
	m[HTTPPrefixPProf+"/symbol"] = http.HandlerFunc(pprof.Symbol)
	m[HTTPPrefixPProf+"/cmdline"] = http.HandlerFunc(pprof.Cmdline)
	m[HTTPPrefixPProf+"/trace "] = http.HandlerFunc(pprof.Trace)
	m[HTTPPrefixPProf+"/heap"] = pprof.Handler("heap")
	m[HTTPPrefixPProf+"/goroutine"] = pprof.Handler("goroutine")
	m[HTTPPrefixPProf+"/threadcreate"] = pprof.Handler("threadcreate")
	m[HTTPPrefixPProf+"/block"] = pprof.Handler("block")
	m[HTTPPrefixPProf+"/mutex"] = pprof.Handler("mutex")

	return m
}

// PProfHandlers returns a map of pprof handlers keyed by the HTTP path.

PProfHandlers returns a map of pprof handlers keyed by the HTTP path .


func NewBackendQuota(s *EtcdServer, name string) Quota {
	lg := s.getLogger()
	quotaBackendBytes.Set(float64(s.Cfg.QuotaBackendBytes))

	if s.Cfg.QuotaBackendBytes < 0 {
		// disable quotas if negative
		quotaLogOnce.Do(func() {
			if lg != nil {
				lg.Info(
					"disabled backend quota",
					zap.String("quota-name", name),
					zap.Int64("quota-size-bytes", s.Cfg.QuotaBackendBytes),
				)
			} else {
				plog.Warningf("disabling backend quota")
			}
		})
		return &passthroughQuota{}
	}

	if s.Cfg.QuotaBackendBytes == 0 {
		// use default size if no quota size given
		quotaLogOnce.Do(func() {
			if lg != nil {
				lg.Info(
					"enabled backend quota with default value",
					zap.String("quota-name", name),
					zap.Int64("quota-size-bytes", DefaultQuotaBytes),
					zap.String("quota-size", DefaultQuotaSize),
				)
			}
		})
		quotaBackendBytes.Set(float64(DefaultQuotaBytes))
		return &backendQuota{s, DefaultQuotaBytes}
	}

	quotaLogOnce.Do(func() {
		if s.Cfg.QuotaBackendBytes > MaxQuotaBytes {
			if lg != nil {
				lg.Warn(
					"quota exceeds the maximum value",
					zap.String("quota-name", name),
					zap.Int64("quota-size-bytes", s.Cfg.QuotaBackendBytes),
					zap.String("quota-size", humanize.Bytes(uint64(s.Cfg.QuotaBackendBytes))),
					zap.Int64("quota-maximum-size-bytes", MaxQuotaBytes),
					zap.String("quota-maximum-size", maxQuotaSize),
				)
			} else {
				plog.Warningf("backend quota %v exceeds maximum recommended quota %v", s.Cfg.QuotaBackendBytes, MaxQuotaBytes)
			}
		}
		if lg != nil {
			lg.Info(
				"enabled backend quota",
				zap.String("quota-name", name),
				zap.Int64("quota-size-bytes", s.Cfg.QuotaBackendBytes),
				zap.String("quota-size", humanize.Bytes(uint64(s.Cfg.QuotaBackendBytes))),
			)
		}
	})
	return &backendQuota{s, s.Cfg.QuotaBackendBytes}
}

// NewBackendQuota creates a quota layer with the given storage limit.

NewBackendQuota creates a quota layer with the given storage limit .


func NewClusterProxy(c *clientv3.Client, advaddr string, prefix string) (pb.ClusterServer, <-chan struct{}) {
	cp := &clusterProxy{
		clus: c.Cluster,
		ctx:  c.Ctx(),
		gr:   &naming.GRPCResolver{Client: c},

		advaddr: advaddr,
		prefix:  prefix,
		umap:    make(map[string]gnaming.Update),
	}

	donec := make(chan struct{})
	if advaddr != "" && prefix != "" {
		go func() {
			defer close(donec)
			cp.resolve(prefix)
		}()
		return cp, donec
	}

	close(donec)
	return cp, donec
}

func NewClusterProxy ( c * clientv3 . Client , advaddr string , prefix string ) ( pb . ClusterServer , <- chan struct { } ) { cp := & clusterProxy { clus : c . Cluster , ctx : c . Ctx ( ) , gr : & naming . GRPCResolver { Client : c } , advaddr : advaddr , prefix : prefix , umap : make ( map [ string ] gnaming . Update ) , } donec := make ( chan struct { } ) if advaddr != " " && prefix != " " { go func ( ) { defer close ( donec ) cp . resolve ( prefix ) } ( ) return cp , donec } close ( donec ) return cp , donec }

// NewClusterProxy takes optional prefix to fetch grpc-proxy member endpoints.
// The returned channel is closed when there is grpc-proxy endpoint registered
// and the client's context is canceled so the 'register' loop returns.

NewClusterProxy takes optional prefix to fetch grpc - proxy member endpoints . The returned channel is closed when there is grpc - proxy endpoint registered and the client s context is canceled so the register loop returns .


func NewHandler(l lease.Lessor, waitch func() <-chan struct{}) http.Handler {
	return &leaseHandler{l, waitch}
}

func NewHandler ( l lease . Lessor , waitch func ( ) <- chan struct { } ) http . Handler { return & leaseHandler { l , waitch } }

// NewHandler returns an http Handler for lease renewals


func TimeToLiveHTTP(ctx context.Context, id lease.LeaseID, keys bool, url string, rt http.RoundTripper) (*leasepb.LeaseInternalResponse, error) {
	// will post lreq protobuf to leader
	lreq, err := (&leasepb.LeaseInternalRequest{
		LeaseTimeToLiveRequest: &pb.LeaseTimeToLiveRequest{
			ID:   int64(id),
			Keys: keys,
		},
	}).Marshal()
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(lreq))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/protobuf")

	req = req.WithContext(ctx)

	cc := &http.Client{Transport: rt}
	var b []byte
	// buffer errc channel so that errc don't block inside the go routinue
	resp, err := cc.Do(req)
	if err != nil {
		return nil, err
	}
	b, err = readResponse(resp)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode == http.StatusRequestTimeout {
		return nil, ErrLeaseHTTPTimeout
	}
	if resp.StatusCode == http.StatusNotFound {
		return nil, lease.ErrLeaseNotFound
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("lease: unknown error(%s)", string(b))
	}

	lresp := &leasepb.LeaseInternalResponse{}
	if err := lresp.Unmarshal(b); err != nil {
		return nil, fmt.Errorf(`lease: %v. data = "%s"`, err, string(b))
	}
	if lresp.LeaseTimeToLiveResponse.ID != int64(id) {
		return nil, fmt.Errorf("lease: renew id mismatch")
	}
	return lresp, nil
}

// TimeToLiveHTTP retrieves lease information of the given lease ID.

TimeToLiveHTTP retrieves lease information of the given lease ID .


func newWatcherBatch(wg *watcherGroup, evs []mvccpb.Event) watcherBatch {
	if len(wg.watchers) == 0 {
		return nil
	}

	wb := make(watcherBatch)
	for _, ev := range evs {
		for w := range wg.watcherSetByKey(string(ev.Kv.Key)) {
			if ev.Kv.ModRevision >= w.minRev {
				// don't double notify
				wb.add(w, ev)
			}
		}
	}
	return wb
}

func
newWatcherBatch
(
wg
*
watcherGroup
,
evs
[
]
mvccpb
.
Event
)
watcherBatch
{
if
len
(
wg
.
watchers
)
==
0
{
return
}
wb
:=
make
(
watcherBatch
)
for
_
,
ev
:=
range
evs
{
for
w
:=
range
wg
.
watcherSetByKey
(
string
(
ev
.
Kv
.
Key
)
)
{
if
ev
.
Kv
.
ModRevision
>=
w
.
minRev
{
// don't double notify
wb
.
add
(
w
,
ev
)
}
}
}
return
wb
}

// newWatcherBatch maps watchers to their matched events. It enables quick
// events look up by watcher.

newWatcherBatch maps watchers to their matched events . It enables quick events look up by watcher .


func (wg *watcherGroup) add(wa *watcher) {
	wg.watchers.add(wa)
	if wa.end == nil {
		wg.keyWatchers.add(wa)
		return
	}

	// interval already registered?
	ivl := adt.NewStringAffineInterval(string(wa.key), string(wa.end))
	if iv := wg.ranges.Find(ivl); iv != nil {
		iv.Val.(watcherSet).add(wa)
		return
	}

	// not registered, put in interval tree
	ws := make(watcherSet)
	ws.add(wa)
	wg.ranges.Insert(ivl, ws)
}

func
(
wg
*
watcherGroup
)
add
(
wa
*
watcher
)
{
wg
.
watchers
.
add
(
wa
)
if
wa
.
end
==
{
wg
.
keyWatchers
.
add
(
wa
)
return
}
// interval already registered?
ivl
:=
adt
.
NewStringAffineInterval
(
string
(
wa
.
key
)
,
string
(
wa
.
end
)
)
if
iv
:=
wg
.
ranges
.
Find
(
ivl
)
;
iv
!=
{
iv
.
Val
.
(
watcherSet
)
.
add
(
wa
)
return
}
// not registered, put in interval tree
ws
:=
make
(
watcherSet
)
ws
.
add
(
wa
)
wg
.
ranges
.
Insert
(
ivl
,
ws
)
}


func (wg *watcherGroup) contains(key string) bool {
	_, ok := wg.keyWatchers[key]
	return ok || wg.ranges.Intersects(adt.NewStringAffinePoint(key))
}

func ( wg * watcherGroup ) contains ( key string ) bool { _ , ok := wg . keyWatchers [ key ] return ok || wg . ranges . Intersects ( adt . NewStringAffinePoint ( key ) ) }

// contains is whether the given key has a watcher in the group.

contains is whether the given key has a watcher in the group .


func (wg *watcherGroup) delete(wa *watcher) bool {
	if _, ok := wg.watchers[wa]; !ok {
		return false
	}
	wg.watchers.delete(wa)
	if wa.end == nil {
		wg.keyWatchers.delete(wa)
		return true
	}

	ivl := adt.NewStringAffineInterval(string(wa.key), string(wa.end))
	iv := wg.ranges.Find(ivl)
	if iv == nil {
		return false
	}

	ws := iv.Val.(watcherSet)
	delete(ws, wa)
	if len(ws) == 0 {
		// remove interval missing watchers
		if ok := wg.ranges.Delete(ivl); !ok {
			panic("could not remove watcher from interval tree")
		}
	}

	return true
}

func
(
wg
*
watcherGroup
)
delete
(
wa
*
watcher
)
bool
{
if
_
,
ok
:=
wg
.
watchers
[
wa
]
;
!
ok
{
return
false
}
wg
.
watchers
.
delete
(
wa
)
if
wa
.
end
==
{
wg
.
keyWatchers
.
delete
(
wa
)
return
true
}
ivl
:=
adt
.
NewStringAffineInterval
(
string
(
wa
.
key
)
,
string
(
wa
.
end
)
)
iv
:=
wg
.
ranges
.
Find
(
ivl
)
if
iv
==
{
return
false
}
ws
:=
iv
.
Val
.
(
watcherSet
)
delete
(
ws
,
wa
)
if
len
(
ws
)
==
0
{
// remove interval missing watchers
if
ok
:=
wg
.
ranges
.
Delete
(
ivl
)
;
!
ok
{
panic
(
"
"
)
}
}
return
true
}

// delete removes a watcher from the group.

delete removes a watcher from the group .


func (wg *watcherGroup) choose(maxWatchers int, curRev, compactRev int64) (*watcherGroup, int64) {
	if len(wg.watchers) < maxWatchers {
		return wg, wg.chooseAll(curRev, compactRev)
	}
	ret := newWatcherGroup()
	for w := range wg.watchers {
		if maxWatchers <= 0 {
			break
		}
		maxWatchers--
		ret.add(w)
	}
	return &ret, ret.chooseAll(curRev, compactRev)
}

func ( wg * watcherGroup ) choose ( maxWatchers int , curRev , compactRev int64 ) ( * watcherGroup , int64 ) { if len ( wg . watchers ) < maxWatchers { return wg , wg . chooseAll ( curRev , compactRev ) } ret := newWatcherGroup ( ) for w := range wg . watchers { if maxWatchers <= 0 { break } maxWatchers -- ret . add ( w ) } return & ret , ret . chooseAll ( curRev , compactRev ) }

// choose selects watchers from the watcher group to update


func (wg *watcherGroup) watcherSetByKey(key string) watcherSet {
	wkeys := wg.keyWatchers[key]
	wranges := wg.ranges.Stab(adt.NewStringAffinePoint(key))

	// zero-copy cases
	switch {
	case len(wranges) == 0:
		// no need to merge ranges or copy; reuse single-key set
		return wkeys
	case len(wranges) == 0 && len(wkeys) == 0:
		return nil
	case len(wranges) == 1 && len(wkeys) == 0:
		return wranges[0].Val.(watcherSet)
	}

	// copy case
	ret := make(watcherSet)
	ret.union(wg.keyWatchers[key])
	for _, item := range wranges {
		ret.union(item.Val.(watcherSet))
	}
	return ret
}

// watcherSetByKey gets the set of watchers that receive events on the given key.

watcherSetByKey gets the set of watchers that receive events on the given key .


func (ivl *Interval) Compare(c Comparable) int {
	ivl2 := c.(*Interval)
	ivbCmpBegin := ivl.Begin.Compare(ivl2.Begin)
	ivbCmpEnd := ivl.Begin.Compare(ivl2.End)
	iveCmpBegin := ivl.End.Compare(ivl2.Begin)

	// ivl is left of ivl2
	if ivbCmpBegin < 0 && iveCmpBegin <= 0 {
		return -1
	}

	// iv is right of iv2
	if ivbCmpEnd >= 0 {
		return 1
	}

	return 0
}

func
(
ivl
*
Interval
)
Compare
(
c
Comparable
)
int
{
ivl2
:=
c
.
(
*
Interval
)
ivbCmpBegin
:=
ivl
.
Begin
.
Compare
(
ivl2
.
Begin
)
ivbCmpEnd
:=
ivl
.
Begin
.
Compare
(
ivl2
.
End
)
iveCmpBegin
:=
ivl
.
End
.
Compare
(
ivl2
.
Begin
)
// ivl is left of ivl2
if
ivbCmpBegin
<
0
&&
iveCmpBegin
<=
0
{
return
1
}
// iv is right of iv2
if
ivbCmpEnd
>=
0
{
return
1
}
return
0
}

// Compare on an interval gives == if the interval overlaps.

Compare on an interval gives == if the interval overlaps .


func (x *intervalNode) successor() *intervalNode {
	if x.right != nil {
		return x.right.min()
	}
	y := x.parent
	for y != nil && x == y.right {
		x = y
		y = y.parent
	}
	return y
}

func ( x * intervalNode ) successor ( ) * intervalNode { if x . right != nil { return x . right . min ( ) } y := x . parent for y != nil && x == y . right { x = y y = y . parent } return y }

// successor is the next in-order node in the tree

successor is the next in - order node in the tree


func (x *intervalNode) updateMax() {
	for x != nil {
		oldmax := x.max
		max := x.iv.Ivl.End
		if x.left != nil && x.left.max.Compare(max) > 0 {
			max = x.left.max
		}
		if x.right != nil && x.right.max.Compare(max) > 0 {
			max = x.right.max
		}
		if oldmax.Compare(max) == 0 {
			break
		}
		x.max = max
		x = x.parent
	}
}

func ( x * intervalNode ) updateMax ( ) { for x != nil { oldmax := x . max max := x . iv . Ivl . End if x . left != nil && x . left . max . Compare ( max ) > 0 { max = x . left . max } if x . right != nil && x . right . max . Compare ( max ) > 0 { max = x . right . max } if oldmax . Compare ( max ) == 0 { break } x . max = max x = x . parent } }

// updateMax updates the maximum values for a node and its ancestors


func (x *intervalNode) visit(iv *Interval, nv nodeVisitor) bool {
	if x == nil {
		return true
	}
	v := iv.Compare(&x.iv.Ivl)
	switch {
	case v < 0:
		if !x.left.visit(iv, nv) {
			return false
		}
	case v > 0:
		maxiv := Interval{x.iv.Ivl.Begin, x.max}
		if maxiv.Compare(iv) == 0 {
			if !x.left.visit(iv, nv) || !x.right.visit(iv, nv) {
				return false
			}
		}
	default:
		if !x.left.visit(iv, nv) || !nv(x) || !x.right.visit(iv, nv) {
			return false
		}
	}
	return true
}

func ( x * intervalNode ) visit ( iv * Interval , nv nodeVisitor ) bool { if x == nil { return true } v := iv . Compare ( & x . iv . Ivl ) switch { case v < 0 : if ! x . left . visit ( iv , nv ) { return false } case v > 0 : maxiv := Interval { x . iv . Ivl . Begin , x . max } if maxiv . Compare ( iv ) == 0 { if ! x . left . visit ( iv , nv ) || ! x . right . visit ( iv , nv ) { return false } } default : if ! x . left . visit ( iv , nv ) || ! nv ( x ) || ! x . right . visit ( iv , nv ) { return false } } return true }

// visit will call a node visitor on each node that overlaps the given interval


func (ivt *IntervalTree) Delete(ivl Interval) bool {
	z := ivt.find(ivl)
	if z == nil {
		return false
	}

	y := z
	if z.left != nil && z.right != nil {
		y = z.successor()
	}

	x := y.left
	if x == nil {
		x = y.right
	}
	if x != nil {
		x.parent = y.parent
	}

	if y.parent == nil {
		ivt.root = x
	} else {
		if y == y.parent.left {
			y.parent.left = x
		} else {
			y.parent.right = x
		}
		y.parent.updateMax()
	}
	if y != z {
		z.iv = y.iv
		z.updateMax()
	}

	if y.color() == black && x != nil {
		ivt.deleteFixup(x)
	}

	ivt.count--
	return true
}

func ( ivt * IntervalTree ) Delete ( ivl Interval ) bool { z := ivt . find ( ivl ) if z == nil { return false } y := z if z . left != nil && z . right != nil { y = z . successor ( ) } x := y . left if x == nil { x = y . right } if x != nil { x . parent = y . parent } if y . parent == nil { ivt . root = x } else { if y == y . parent . left { y . parent . left = x } else { y . parent . right = x } y . parent . updateMax ( ) } if y != z { z . iv = y . iv z . updateMax ( ) } if y . color ( ) == black && x != nil { ivt . deleteFixup ( x ) } ivt . count -- return true }

// Delete removes the node with the given interval from the tree, returning
// true if a node is in fact removed.

Delete removes the node with the given interval from the tree returning true if a node is in fact removed .


func (ivt *IntervalTree) Insert(ivl Interval, val interface{}) {
	var y *intervalNode
	z := &intervalNode{iv: IntervalValue{ivl, val}, max: ivl.End, c: red}
	x := ivt.root
	for x != nil {
		y = x
		if z.iv.Ivl.Begin.Compare(x.iv.Ivl.Begin) < 0 {
			x = x.left
		} else {
			x = x.right
		}
	}

	z.parent = y
	if y == nil {
		ivt.root = z
	} else {
		if z.iv.Ivl.Begin.Compare(y.iv.Ivl.Begin) < 0 {
			y.left = z
		} else {
			y.right = z
		}
		y.updateMax()
	}
	z.c = red
	ivt.insertFixup(z)
	ivt.count++
}

func ( ivt * IntervalTree ) Insert ( ivl Interval , val interface { } ) { var y * intervalNode z := & intervalNode { iv : IntervalValue { ivl , val } , max : ivl . End , c : red } x := ivt . root for x != nil { y = x if z . iv . Ivl . Begin . Compare ( x . iv . Ivl . Begin ) < 0 { x = x . left } else { x = x . right } } z . parent = y if y == nil { ivt . root = z } else { if z . iv . Ivl . Begin . Compare ( y . iv . Ivl . Begin ) < 0 { y . left = z } else { y . right = z } y . updateMax ( ) } z . c = red ivt . insertFixup ( z ) ivt . count ++ }

// Insert adds a node with the given interval into the tree.

Insert adds a node with the given interval into the tree .


func (ivt *IntervalTree) rotateLeft(x *intervalNode) {
	y := x.right
	x.right = y.left
	if y.left != nil {
		y.left.parent = x
	}
	x.updateMax()
	ivt.replaceParent(x, y)
	y.left = x
	y.updateMax()
}

func ( ivt * IntervalTree ) rotateLeft ( x * intervalNode ) { y := x . right x . right = y . left if y . left != nil { y . left . parent = x } x . updateMax ( ) ivt . replaceParent ( x , y ) y . left = x y . updateMax ( ) }

// rotateLeft moves x so it is left of its right child


func (ivt *IntervalTree) replaceParent(x *intervalNode, y *intervalNode) {
	y.parent = x.parent
	if x.parent == nil {
		ivt.root = y
	} else {
		if x == x.parent.left {
			x.parent.left = y
		} else {
			x.parent.right = y
		}
		x.parent.updateMax()
	}
	x.parent = y
}

func ( ivt * IntervalTree ) replaceParent ( x * intervalNode , y * intervalNode ) { y . parent = x . parent if x . parent == nil { ivt . root = y } else { if x == x . parent . left { x . parent . left = y } else { x . parent . right = y } x . parent . updateMax ( ) } x . parent = y }

// replaceParent replaces x's parent with y

replaceParent replaces x s parent with y


func (ivt *IntervalTree) MaxHeight() int {
	return int((2 * math.Log2(float64(ivt.Len()+1))) + 0.5)
}

func ( ivt * IntervalTree ) MaxHeight ( ) int { return int ( ( 2 * math . Log2 ( float64 ( ivt . Len ( ) + 1 ) ) ) + 0.5 ) }

// MaxHeight is the expected maximum tree height given the number of nodes