func (c *conn) DeleteWithChildren(pathcur string) error {
	deleteFn := func(pathcur string, data []byte) error {
		return c.Delete(pathcur, int32(-1))
	}
	err := c.walk(pathcur, deleteFn)
	return err
}
DeleteWithChildren delete path and its children if necessary
package zk
import (
	"bytes"
	"errors"
	"fmt"
	"math/rand"
	"net"
	"path"
	"time"
	"github.com/mesosphere/go-zookeeper/zk"
	"go.uber.org/atomic"
	"github.com/samaritan-proxy/sash/logger"
)
var (
	errConnectTimeout = errors.New("zk connect timeout")
)
func init() {
	rand.Seed(time.Now().UnixNano())
}
type ConnConfig struct {
	Hosts          []string      `yaml:"hosts"`
	User           string        `yaml:"user"`
	Pwd            string        `yaml:"pwd"`
	ConnectTimeout time.Duration `yaml:"connect_timeout"`
	SessionTimeout time.Duration `yaml:"session_timeout"`
	BasePath       string        `yaml:"base_path"`
}
func (cfg *ConnConfig) auth() string {
	if cfg.User != "" || cfg.Pwd != "" {
		return fmt.Sprintf("%s:%s", cfg.User, cfg.Pwd)
	}
	return ""
}
type Conn interface {
	Get(path string) ([]byte, *zk.Stat, error)
	GetW(path string) ([]byte, *zk.Stat, <-chan zk.Event, error)
	Children(path string) ([]string, *zk.Stat, error)
	ChildrenW(path string) ([]string, *zk.Stat, <-chan zk.Event, error)
	Exists(path string) (bool, *zk.Stat, error)
	CreateRecursively(p string, data []byte) error
	DeleteWithChildren(pathcur string) error
	Update() <-chan zk.Event
	Close()
}
func CreateConn(cfg *ConnConfig, waitConnected bool) (Conn, error) {
	return createConn(cfg, waitConnected)
}
type conn struct {
	*zk.Conn
	update    <-chan zk.Event
	cfg       *ConnConfig
	authAdded *atomic.Bool
}
func createConn(cfg *ConnConfig, waitConnected bool) (*conn, error) {
	if cfg.SessionTimeout <= 0 {
		cfg.SessionTimeout = time.Second
	}
	if cfg.ConnectTimeout <= 0 {
		cfg.ConnectTimeout = time.Second
	}
	var err error
	c := &conn{
		cfg:       cfg,
		authAdded: atomic.NewBool(false),
	}
	c.Conn, c.update, err = zk.Connect(cfg.Hosts, cfg.SessionTimeout,
		func(c *zk.Conn) {
			c.SetLogger(logger.Get())
		},
		zk.WithDialer(zk.Dialer(func(network, address string, timeout time.Duration) (net.Conn, error) {
			return net.DialTimeout(network, address, cfg.ConnectTimeout)
		})))
	if err != nil {
		return nil, err
	}
	err = c.addAuth()
	if err != nil {
		return nil, err
	}
	if waitConnected {
		err = c.waitConnected()
	}
	return c, err
}
func (c *conn) Authed() bool {
	return c.authAdded.Load()
}
var addAuth = func(conn *zk.Conn, scheme string, auth []byte) error {
	return conn.AddAuth(scheme, auth)
}
func (c *conn) addAuth() error {
	if c.cfg.auth() != "" && !c.Authed() {
		logger.Debug("Authenticating with: ", c.cfg.auth())
		err := addAuth(c.Conn, "digest", []byte(c.cfg.auth()))
		if err == nil {
			c.authAdded.Store(true)
		}
		return err
	}
	return nil
}
func (c *conn) waitConnected() error {
	attempt := 0
	for {
		e := <-c.update
		switch e.State {
		case zk.StateConnected, zk.StateHasSession:
			logger.Info("Zookeeper connection established")
			return nil
		case zk.StateConnecting:
			attempt++
		}
		if attempt >= 2 {
			break
		}
	}
	return errConnectTimeout
}
func (c *conn) CreateRecursively(p string, data []byte) error {
	err := c.createParentRecursively(p)
	if err != nil {
		return err
	}
	isEqual, err := c.nodeEqual(p, data)
	if err != nil {
		_, err = c.Create(p, data, 0, c.getACL())
		return err
	}
	if !isEqual {
		_, err = c.Set(p, data, -1)
	}
	return err
}
func (c *conn) createParentRecursively(p string) error {
	var parent = path.Dir(p)
	if parent == p {
		return nil
	}
	existed, _, err := c.Exists(parent)
	if err != nil {
		return err
	}
	if existed {
		return nil
	}
	if err = c.createParentRecursively(parent); err != nil {
		return err
	}
	_, err = c.Create(parent, []byte(""), 0, c.getACL())
	return err
}
func (c *conn) nodeEqual(p string, data []byte) (bool, error) {
	existData, _, err := c.Get(p)
	if err != nil {
		return false, err
	}
	return bytes.Equal(data, existData), nil
}
func (c *conn) getACL() []zk.ACL {
	var acl = zk.WorldACL(zk.PermAll)
	if c.cfg.auth() != "" {
		acl = zk.DigestACL(zk.PermAll, c.cfg.User, c.cfg.Pwd)
	}
	return acl
}