func (c *EtcdClient) Apply(ctx context.Context, d *model.KVPair) (*model.KVPair, error) {
	return c.set(d, etcdApplyOpts)
}
Set an existing entry in the datastore.  This ignores whether an entry already
exists.
package etcd
import (
	goerrors "errors"
	"fmt"
	"strconv"
	"strings"
	"time"
	log "github.com/sirupsen/logrus"
	etcd "go.etcd.io/etcd/client"
	"go.etcd.io/etcd/pkg/srv"
	"go.etcd.io/etcd/pkg/transport"
	"golang.org/x/net/context"
	v1 "github.com/projectcalico/libcalico-go/lib/apis/v1"
	"github.com/projectcalico/libcalico-go/lib/backend/api"
	"github.com/projectcalico/libcalico-go/lib/backend/model"
	"github.com/projectcalico/libcalico-go/lib/errors"
)
var (
	etcdApplyOpts        = &etcd.SetOptions{PrevExist: etcd.PrevIgnore}
	etcdCreateOpts       = &etcd.SetOptions{PrevExist: etcd.PrevNoExist}
	etcdCreateDirOpts    = &etcd.SetOptions{PrevExist: etcd.PrevNoExist, Dir: true}
	etcdDeleteEmptyOpts  = &etcd.DeleteOptions{Recursive: false, Dir: true}
	etcdGetOpts          = &etcd.GetOptions{Quorum: true}
	etcdListOpts         = &etcd.GetOptions{Quorum: true, Recursive: true, Sort: true}
	etcdListChildrenOpts = &etcd.GetOptions{Quorum: true, Recursive: false, Sort: true}
	clientTimeout        = 30 * time.Second
)
type EtcdClient struct {
	etcdClient  etcd.Client
	etcdKeysAPI etcd.KeysAPI
}
func NewEtcdClient(config *v1.EtcdConfig) (*EtcdClient, error) {
	if (config.EtcdAuthority != "" || config.EtcdEndpoints != "") && config.EtcdDiscoverySrv != "" {
		return nil, goerrors.New("multiple discovery or bootstrap options specified, use either \"etcdEndpoints\" or \"etcdDiscoverySrv\"")
	}
	etcdLocation := []string{}
	if config.EtcdAuthority != "" {
		etcdLocation = []string{config.EtcdScheme + "://" + config.EtcdAuthority}
	}
	if config.EtcdEndpoints != "" {
		etcdLocation = strings.Split(config.EtcdEndpoints, ",")
	}
	if config.EtcdDiscoverySrv != "" {
		srvs, srvErr := srv.GetClient("etcd-client", config.EtcdDiscoverySrv, "")
		if srvErr != nil {
			return nil, fmt.Errorf("failed to discover etcd endpoints through SRV discovery: %v", srvErr)
		}
		etcdLocation = srvs.Endpoints
	}
	if len(etcdLocation) == 0 {
		return nil, goerrors.New("no etcd authority or endpoints specified")
	}
	tls := transport.TLSInfo{
		TrustedCAFile: config.EtcdCACertFile,
		CertFile:      config.EtcdCertFile,
		KeyFile:       config.EtcdKeyFile,
	}
	transport, err := transport.NewTransport(tls, clientTimeout)
	if err != nil {
		return nil, err
	}
	cfg := etcd.Config{
		Endpoints:               etcdLocation,
		Transport:               transport,
		HeaderTimeoutPerRequest: clientTimeout,
	}
	if config.EtcdUsername != "" && config.EtcdPassword != "" {
		cfg.Username = config.EtcdUsername
		cfg.Password = config.EtcdPassword
	}
	client, err := etcd.New(cfg)
	if err != nil {
		return nil, err
	}
	keys := etcd.NewKeysAPI(client)
	return &EtcdClient{etcdClient: client, etcdKeysAPI: keys}, nil
}
func (c *EtcdClient) EnsureInitialized() error {
	kv := &model.KVPair{
		Key:   model.ReadyFlagKey{},
		Value: true,
	}
	if _, err := c.Create(kv); err == nil {
		log.Info("Ready flag is now set")
	} else {
		if _, ok := err.(errors.ErrorResourceAlreadyExists); !ok {
			log.WithError(err).Warn("Failed to set ready flag")
			return err
		}
		log.Info("Ready flag is already set")
	}
	return nil
}
func (c *EtcdClient) EnsureCalicoNodeInitialized(node string) error {
	dirs := []string{
		"/calico/v1/ipam/v4/pool",
		"/calico/v1/ipam/v6/pool",
		"/calico/bgp/v1/global/custom_filters/v4",
		"/calico/bgp/v1/global/custom_filters/v6",
		"/calico/ipam/v2/host/" + node + "/ipv4/block",
		"/calico/ipam/v2/host/" + node + "/ipv6/block",
	}
	for _, d := range dirs {
		if err := c.ensureDirectory(d); err != nil {
			return err
		}
	}
	return nil
}
func (c *EtcdClient) Syncer(callbacks api.SyncerCallbacks) api.Syncer {
	return newSyncer(c.etcdKeysAPI, callbacks)
}
func (c *EtcdClient) Create(d *model.KVPair) (*model.KVPair, error) {
	return c.set(d, etcdCreateOpts)
}
func (c *EtcdClient) Update(d *model.KVPair) (*model.KVPair, error) {
	options := etcd.SetOptions{PrevExist: etcd.PrevExist}
	if len(d.Revision) != 0 {
		var err error
		if options.PrevIndex, err = strconv.ParseUint(d.Revision, 10, 64); err != nil {
			return nil, err
		}
		log.Debugf("Performing CAS against etcd index: %v\n", options.PrevIndex)
	}
	return c.set(d, &options)
}