1
0
mirror of https://github.com/containous/traefik.git synced 2024-12-22 13:34:03 +03:00

Migrate from libkv to valkeyrie library

This commit is contained in:
NicoMen 2018-01-24 17:52:03 +01:00 committed by Traefiker
parent a91080b060
commit 563a0bd274
33 changed files with 109 additions and 115 deletions

37
Gopkg.lock generated
View File

@ -126,6 +126,20 @@
revision = "65b0cdae8d7fe5c05c7430e055938ef6d24a66c9"
source = "github.com/containous/go-http-auth"
[[projects]]
branch = "master"
name = "github.com/abronan/valkeyrie"
packages = [
".",
"store",
"store/boltdb",
"store/consul",
"store/etcd/v2",
"store/etcd/v3",
"store/zookeeper"
]
revision = "063d875e3c5fd734fa2aa12fac83829f62acfc70"
[[projects]]
name = "github.com/aokoli/goutils"
packages = ["."]
@ -235,8 +249,8 @@
[[projects]]
name = "github.com/containous/staert"
packages = ["."]
revision = "af517d5b70db9c4b0505e0144fcc62b054057d2a"
version = "v2.0.0"
revision = "dbb7c840f31daec0d863ada6829d075a8dbb7469"
version = "v3.0.0"
[[projects]]
name = "github.com/containous/traefik-extra-service-fabric"
@ -427,7 +441,7 @@
branch = "master"
name = "github.com/docker/leadership"
packages = ["."]
revision = "af20da7d3e62be9259835e93261acf931b5adecf"
revision = "a2e096d9fe0af5b4c37dd37aea719bc9c2e5eec6"
source = "github.com/containous/leadership"
[[projects]]
@ -457,21 +471,6 @@
]
revision = "57bd716502dcbe1799f026148016022b0f3b989c"
[[projects]]
branch = "master"
name = "github.com/docker/libkv"
packages = [
".",
"store",
"store/boltdb",
"store/consul",
"store/etcd/v2",
"store/etcd/v3",
"store/zookeeper"
]
revision = "5e4bb288a9a74320bb03f5c18d6bdbab0d8049de"
source = "github.com/abronan/libkv"
[[projects]]
name = "github.com/donovanhide/eventsource"
packages = ["."]
@ -1513,6 +1512,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "abd30ec16123cf6de5a869dcaba3e3247907785b4802f0eebaa0cf4d16ef444b"
inputs-digest = "0d3d2cd01e06cfcc86cbd261395d59187d4d27d88398bd7aeb1298676becd3e7"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -62,7 +62,7 @@
[[constraint]]
name = "github.com/containous/staert"
version = "2.0.0"
version = "3.0.0"
[[constraint]]
name = "github.com/containous/traefik-extra-service-fabric"
@ -77,10 +77,6 @@
name = "github.com/docker/leadership"
source = "github.com/containous/leadership"
[[constraint]]
name = "github.com/docker/libkv"
source = "github.com/abronan/libkv"
[[constraint]]
name = "github.com/eapache/channels"
version = "1.1.0"
@ -115,6 +111,10 @@
branch = "master"
name = "github.com/jjcollinge/servicefabric"
[[constraint]]
branch = "master"
name = "github.com/abronan/valkeyrie"
[[constraint]]
name = "github.com/mattn/go-shellwords"
version = "1.0.3"

View File

@ -7,12 +7,12 @@ import (
"sync"
"time"
"github.com/abronan/valkeyrie/store"
"github.com/cenk/backoff"
"github.com/containous/staert"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/docker/libkv/store"
"github.com/satori/go.uuid"
)

View File

@ -5,11 +5,11 @@ import (
"fmt"
stdlog "log"
"github.com/abronan/valkeyrie/store"
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/cluster"
"github.com/docker/libkv/store"
)
func newStoreConfigCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {

View File

@ -10,13 +10,13 @@ import (
"sync"
"time"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/consul"
"github.com/containous/staert"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/integration/try"
"github.com/containous/traefik/types"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/consul"
"github.com/go-check/check"
checker "github.com/vdemeester/shakers"
)
@ -32,7 +32,7 @@ func (s *ConsulSuite) setupConsul(c *check.C) {
s.composeProject.Start(c)
consul.Register()
kv, err := libkv.NewStore(
kv, err := valkeyrie.NewStore(
store.CONSUL,
[]string{s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8500"},
&store.Config{
@ -63,7 +63,7 @@ func (s *ConsulSuite) setupConsulTLS(c *check.C) {
TLSConfig, err := clientTLS.CreateTLSConfig()
c.Assert(err, checker.IsNil)
kv, err := libkv.NewStore(
kv, err := valkeyrie.NewStore(
store.CONSUL,
[]string{s.composeProject.Container(c, "consul").NetworkSettings.IPAddress + ":8585"},
&store.Config{

View File

@ -8,10 +8,10 @@ import (
"strings"
"time"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/etcd/v3"
"github.com/containous/traefik/integration/try"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/etcd/v3"
"github.com/go-check/check"
checker "github.com/vdemeester/shakers"
@ -41,7 +41,7 @@ func (s *Etcd3Suite) SetUpTest(c *check.C) {
etcdv3.Register()
url := ipEtcd + ":2379"
kv, err := libkv.NewStore(
kv, err := valkeyrie.NewStore(
store.ETCDV3,
[]string{url},
&store.Config{

View File

@ -8,10 +8,10 @@ import (
"strings"
"time"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/etcd/v2"
"github.com/containous/traefik/integration/try"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/etcd/v2"
"github.com/go-check/check"
checker "github.com/vdemeester/shakers"
@ -29,7 +29,7 @@ func (s *EtcdSuite) SetUpTest(c *check.C) {
etcd.Register()
url := s.composeProject.Container(c, "etcd").NetworkSettings.IPAddress + ":2379"
kv, err := libkv.NewStore(
kv, err := valkeyrie.NewStore(
store.ETCD,
[]string{url},
&store.Config{

View File

@ -7,7 +7,7 @@ import (
"net/http"
"strings"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie/store"
)
// ResponseCondition is a retry condition function.

View File

@ -3,12 +3,12 @@ package boltdb
import (
"fmt"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/boltdb"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/kv"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/boltdb"
)
var _ provider.Provider = (*Provider)(nil)
@ -23,7 +23,7 @@ type Provider struct {
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := p.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)
return fmt.Errorf("failed to Connect to KV store: %v", err)
}
p.SetKVClient(store)
return p.Provider.Provide(configurationChan, pool, constraints)

View File

@ -3,12 +3,12 @@ package consul
import (
"fmt"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/consul"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/kv"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/consul"
)
var _ provider.Provider = (*Provider)(nil)
@ -23,7 +23,7 @@ type Provider struct {
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := p.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)
return fmt.Errorf("failed to Connect to KV store: %v", err)
}
p.SetKVClient(store)
return p.Provider.Provide(configurationChan, pool, constraints)

View File

@ -3,14 +3,14 @@ package etcd
import (
"fmt"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/etcd/v2"
"github.com/abronan/valkeyrie/store/etcd/v3"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/kv"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/etcd/v2"
"github.com/docker/libkv/store/etcd/v3"
)
var _ provider.Provider = (*Provider)(nil)
@ -26,7 +26,7 @@ type Provider struct {
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := p.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)
return fmt.Errorf("failed to Connect to KV store: %v", err)
}
p.SetKVClient(store)
return p.Provider.Provide(configurationChan, pool, constraints)

View File

@ -5,7 +5,7 @@ import (
"strings"
"testing"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie/store"
"github.com/stretchr/testify/assert"
)

View File

@ -6,14 +6,14 @@ import (
"strings"
"time"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
)
// Provider holds common configurations of key-value providers.
@ -44,7 +44,7 @@ func (p *Provider) CreateStore() (store.Store, error) {
return nil, err
}
}
return libkv.NewStore(
return valkeyrie.NewStore(
p.storeType,
strings.Split(p.Endpoint, ","),
storeConfig,

View File

@ -10,12 +10,12 @@ import (
"text/template"
"github.com/BurntSushi/ty/fun"
"github.com/abronan/valkeyrie/store"
"github.com/containous/flaeg"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider/label"
"github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
)
func (p *Provider) buildConfiguration() *types.Configuration {

View File

@ -6,11 +6,11 @@ import (
"testing"
"time"
"github.com/abronan/valkeyrie/store"
"github.com/containous/flaeg"
"github.com/containous/traefik/provider/label"
"github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
"github.com/stretchr/testify/assert"
)

View File

@ -4,7 +4,7 @@ import (
"errors"
"strings"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie/store"
)
func newProviderMock(kvPairs []*store.KVPair) *Provider {

View File

@ -4,8 +4,8 @@ import (
"testing"
"time"
"github.com/abronan/valkeyrie/store"
"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
)
func TestKvWatchTree(t *testing.T) {

View File

@ -3,12 +3,12 @@ package zk
import (
"fmt"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/zookeeper"
"github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/kv"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/libkv/store"
"github.com/docker/libkv/store/zookeeper"
)
var _ provider.Provider = (*Provider)(nil)
@ -23,7 +23,7 @@ type Provider struct {
func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
store, err := p.CreateStore()
if err != nil {
return fmt.Errorf("Failed to Connect to KV store: %v", err)
return fmt.Errorf("failed to Connect to KV store: %v", err)
}
p.SetKVClient(store)
return p.Provider.Provide(configurationChan, pool, constraints)

View File

@ -11,10 +11,10 @@ import (
"strconv"
"strings"
"github.com/abronan/valkeyrie/store"
"github.com/containous/flaeg"
"github.com/containous/traefik/log"
traefikTls "github.com/containous/traefik/tls"
"github.com/docker/libkv/store"
"github.com/ryanuber/go-glob"
)

View File

@ -10,9 +10,9 @@ import (
"sync/atomic"
"time"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
"github.com/coreos/bbolt"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
)
var (
@ -34,7 +34,7 @@ type BoltDB struct {
dbIndex uint64
path string
timeout time.Duration
// By default libkv opens and closes the bolt DB connection for every
// By default valkeyrie opens and closes the bolt DB connection for every
// get/put operation. This allows multiple apps to use a Bolt DB at the
// same time.
// PersistConnection flag provides an option to override ths behavior.
@ -44,13 +44,13 @@ type BoltDB struct {
}
const (
libkvmetadatalen = 8
metadatalen = 8
transientTimeout = time.Duration(10) * time.Second
)
// Register registers boltdb to libkv
// Register registers boltdb to valkeyrie
func Register() {
libkv.AddStore(store.BOLTDB, New)
valkeyrie.AddStore(store.BOLTDB, New)
}
// New opens a new BoltDB connection to the specified path and bucket
@ -126,7 +126,7 @@ func (b *BoltDB) releaseDBhandle() {
}
// Get the value at "key". BoltDB doesn't provide an inbuilt last modified index with every kv pair. Its implemented by
// by a atomic counter maintained by the libkv and appened to the value passed by the client.
// by a atomic counter maintained by the valkeyrie and appened to the value passed by the client.
func (b *BoltDB) Get(key string, opts *store.ReadOptions) (*store.KVPair, error) {
var (
val []byte
@ -161,8 +161,8 @@ func (b *BoltDB) Get(key string, opts *store.ReadOptions) (*store.KVPair, error)
return nil, err
}
dbIndex := binary.LittleEndian.Uint64(val[:libkvmetadatalen])
val = val[libkvmetadatalen:]
dbIndex := binary.LittleEndian.Uint64(val[:metadatalen])
val = val[metadatalen:]
return &store.KVPair{Key: key, Value: val, LastIndex: (dbIndex)}, nil
}
@ -177,7 +177,7 @@ func (b *BoltDB) Put(key string, value []byte, opts *store.WriteOptions) error {
b.Lock()
defer b.Unlock()
dbval := make([]byte, libkvmetadatalen)
dbval := make([]byte, metadatalen)
if db, err = b.getDBhandle(); err != nil {
return err
@ -287,8 +287,8 @@ func (b *BoltDB) List(keyPrefix string, opts *store.ReadOptions) ([]*store.KVPai
for key, v := cursor.Seek(prefix); bytes.HasPrefix(key, prefix); key, v = cursor.Next() {
hasResult = true
dbIndex := binary.LittleEndian.Uint64(v[:libkvmetadatalen])
v = v[libkvmetadatalen:]
dbIndex := binary.LittleEndian.Uint64(v[:metadatalen])
v = v[metadatalen:]
val := make([]byte, len(v))
copy(val, v)
@ -338,7 +338,7 @@ func (b *BoltDB) AtomicDelete(key string, previous *store.KVPair) (bool, error)
if val == nil {
return store.ErrKeyNotFound
}
dbIndex := binary.LittleEndian.Uint64(val[:libkvmetadatalen])
dbIndex := binary.LittleEndian.Uint64(val[:metadatalen])
if dbIndex != previous.LastIndex {
return store.ErrKeyModified
}
@ -363,7 +363,7 @@ func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, opt
b.Lock()
defer b.Unlock()
dbval := make([]byte, libkvmetadatalen)
dbval := make([]byte, metadatalen)
if db, err = b.getDBhandle(); err != nil {
return false, nil, err
@ -392,7 +392,7 @@ func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, opt
if len(val) == 0 {
return store.ErrKeyNotFound
}
dbIndex = binary.LittleEndian.Uint64(val[:libkvmetadatalen])
dbIndex = binary.LittleEndian.Uint64(val[:metadatalen])
if dbIndex != previous.LastIndex {
return store.ErrKeyModified
}

View File

@ -8,8 +8,8 @@ import (
"sync"
"time"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
api "github.com/hashicorp/consul/api"
)
@ -55,9 +55,9 @@ type consulLock struct {
renewCh chan struct{}
}
// Register registers consul to libkv
// Register registers consul to valkeyrie
func Register() {
libkv.AddStore(store.CONSUL, New)
valkeyrie.AddStore(store.CONSUL, New)
}
// New creates a new Consul client given a list

View File

@ -12,9 +12,9 @@ import (
"golang.org/x/net/context"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
etcd "github.com/coreos/etcd/client"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
)
const (
@ -53,9 +53,9 @@ const (
defaultUpdateTime = 5 * time.Second
)
// Register registers etcd to libkv
// Register registers etcd to valkeyrie
func Register() {
libkv.AddStore(store.ETCD, New)
valkeyrie.AddStore(store.ETCD, New)
}
// New creates a new Etcd client given a list

View File

@ -7,10 +7,10 @@ import (
"sync"
"time"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
etcd "github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/clientv3/concurrency"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
)
const (
@ -38,9 +38,9 @@ type etcdLock struct {
ttl time.Duration
}
// Register registers etcd to libkv
// Register registers etcd to valkeyrie
func Register() {
libkv.AddStore(store.ETCDV3, New)
valkeyrie.AddStore(store.ETCDV3, New)
}
// New creates a new Etcd client given a list

View File

@ -25,7 +25,7 @@ const (
)
var (
// ErrBackendNotSupported is thrown when the backend k/v store is not supported by libkv
// ErrBackendNotSupported is thrown when the backend k/v store is not supported by valkeyrie
ErrBackendNotSupported = errors.New("Backend storage not supported yet, please choose one of")
// ErrCallNotSupported is thrown when a method is not implemented/supported by the current backend
ErrCallNotSupported = errors.New("The current call is not supported with this backend")
@ -66,7 +66,7 @@ type ClientTLSConfig struct {
// Store represents the backend K/V storage
// Each store should support every call listed
// here. Or it couldn't be implemented as a K/V
// backend for libkv
// backend for valkeyrie
type Store interface {
// Put a value at the specified key
Put(key string, value []byte, options *WriteOptions) error

View File

@ -4,8 +4,8 @@ import (
"strings"
"time"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
zk "github.com/samuel/go-zookeeper/zk"
)
@ -32,9 +32,9 @@ type zookeeperLock struct {
value []byte
}
// Register registers zookeeper to libkv
// Register registers zookeeper to valkeyrie
func Register() {
libkv.AddStore(store.ZK, New)
valkeyrie.AddStore(store.ZK, New)
}
// New creates a new Zookeeper client given a
@ -485,7 +485,7 @@ func (s *Zookeeper) get(key string) ([]byte, *zk.Stat, error) {
var meta *zk.Stat
var err error
// To guard against older versions of libkv
// To guard against older versions of valkeyrie
// creating and writing to znodes non-atomically,
// We try to resync few times if we read SOH or
// an empty string
@ -518,7 +518,7 @@ func (s *Zookeeper) getW(key string) ([]byte, *zk.Stat, <-chan zk.Event, error)
var ech <-chan zk.Event
var err error
// To guard against older versions of libkv
// To guard against older versions of valkeyrie
// creating and writing to znodes non-atomically,
// We try to resync few times if we read SOH or
// an empty string

View File

@ -1,11 +1,11 @@
package libkv
package valkeyrie
import (
"fmt"
"sort"
"strings"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie/store"
)
// Initialize creates a new Store object, initializing the client
@ -34,7 +34,7 @@ func NewStore(backend store.Backend, addrs []string, options *store.Config) (sto
return nil, fmt.Errorf("%s %s", store.ErrBackendNotSupported.Error(), supportedBackend)
}
// AddStore adds a new store backend to libkv
// AddStore adds a new store backend to valkeyrie
func AddStore(store store.Backend, init Initialize) {
initializers[store] = init
}

View File

@ -10,9 +10,9 @@ import (
"strconv"
"strings"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
"github.com/containous/flaeg"
"github.com/docker/libkv"
"github.com/docker/libkv/store"
"github.com/mitchellh/mapstructure"
)
@ -27,11 +27,11 @@ type KvSource struct {
// NewKvSource creates a new KvSource
func NewKvSource(backend store.Backend, addrs []string, options *store.Config, prefix string) (*KvSource, error) {
kvStore, err := libkv.NewStore(backend, addrs, options)
kvStore, err := valkeyrie.NewStore(backend, addrs, options)
return &KvSource{Store: kvStore, Prefix: prefix}, err
}
// Parse uses libkv and mapstructure to fill the structure
// Parse uses valkeyrie and mapstructure to fill the structure
func (kv *KvSource) Parse(cmd *flaeg.Command) (*flaeg.Command, error) {
err := kv.LoadConfig(cmd.Config)
if err != nil {

View File

@ -105,14 +105,8 @@ func (ts *TomlSource) ConfigFileUsed() string {
func preprocessDir(dirIn string) (string, error) {
dirOut := dirIn
if strings.HasPrefix(dirIn, "$") {
end := strings.Index(dirIn, string(os.PathSeparator))
if end == -1 {
end = len(dirIn)
}
dirOut = os.Getenv(dirIn[1:end]) + dirIn[end:]
}
dirOut, err := filepath.Abs(dirOut)
expanded := os.ExpandEnv(dirIn)
dirOut, err := filepath.Abs(expanded)
return dirOut, err
}
@ -123,7 +117,8 @@ func findFile(filename string, dirNfile []string) string {
if fileInfo, err := os.Stat(fullPath); err == nil && !fileInfo.IsDir() {
return fullPath
}
fullPath = fullPath + "/" + filename + ".toml"
fullPath = filepath.Join(fullPath, filename+".toml")
if fileInfo, err := os.Stat(fullPath); err == nil && !fileInfo.IsDir() {
return fullPath
}

View File

@ -4,7 +4,7 @@ import (
"sync"
"time"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie/store"
)
const (

View File

@ -3,7 +3,7 @@ package leadership
import (
"errors"
"github.com/docker/libkv/store"
"github.com/abronan/valkeyrie/store"
)
// Follower can follow an election in real-time and push notifications whenever