package etcd import ( "context" "errors" "strings" "time" "github.com/featurebasedb/featurebase/v3/disco" clientv3 "go.etcd.io/etcd/client/v3" ) // TODO (twg) test coverage once we figure out how to setup external etcd in test type ExternalEtcd struct { parent *Etcd id string peers []*disco.Peer etcdHosts []string } func NewExternalEtcd(p *Etcd, o Options) (*ExternalEtcd, error) { if o.Cluster == "" { return nil, errors.New("cluster required") } if o.Id == "" { return nil, errors.New("nodeid required") } if o.EtcdHosts == "" { return nil, errors.New("remote etcd host required") } // %% begin sonarcloud ignore %% nodes := strings.Split(o.Cluster, ",") // id=url,id2=url2,....,idn=urln peers := make([]*disco.Peer, len(nodes)) for i, node := range nodes { parts := strings.Split(node, "=") // id=url pairs peers[i] = &disco.Peer{ID: parts[0], URL: parts[1]} } this := &ExternalEtcd{} this.parent = p this.peers = peers this.id = o.Id // populate parent.KnownNodes for _, n := range peers { this.parent.seeNode(n.ID) } this.parent.nodesDirty = true // populate parent.sortedNodes hosts := strings.Split(o.EtcdHosts, ",") this.etcdHosts = make([]string, len(hosts)) copy(this.etcdHosts, hosts) return this, nil // %% end sonarcloud ignore %% } func (e *ExternalEtcd) ID() string { return e.id } func (e *ExternalEtcd) Shutdown() { } func (e *ExternalEtcd) Peers() []*disco.Peer { return e.peers } // TODO this is a nonsense implementation right now // will fill out when understand the issue func (e *ExternalEtcd) IsLeader() bool { return e.Leader().String() == e.ID() } func (e *ExternalEtcd) Leader() *disco.Peer { // TODO (twg) NOP? return e.peers[0] } func (e *ExternalEtcd) Startup(ctx context.Context, state disco.InitialClusterState) (disco.InitialClusterState, error) { // TODO (twg) trye disabled return state, e.parent.startHeartbeatAndWatcher(ctx) } func (e *ExternalEtcd) NewClient() (*clientv3.Client, error) { return clientv3.New( clientv3.Config{ DialTimeout: 10 * time.Second, Endpoints: e.etcdHosts, }, ) }