github.com/decred/dcrlnd@v0.7.6/cluster/etcd_elector_test.go (about) 1 //go:build kvdb_etcd 2 // +build kvdb_etcd 3 4 package cluster 5 6 import ( 7 "context" 8 "io/ioutil" 9 "os" 10 "runtime/pprof" 11 "sync" 12 "testing" 13 "time" 14 15 "github.com/decred/dcrlnd/kvdb/etcd" 16 "github.com/stretchr/testify/require" 17 ) 18 19 // GuardTimeout implements a test level timeout guard. 20 func GuardTimeout(t *testing.T, timeout time.Duration) func() { 21 done := make(chan struct{}) 22 go func() { 23 select { 24 case <-time.After(timeout): 25 err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) 26 require.NoError(t, err) 27 panic("test timeout") 28 29 case <-done: 30 } 31 }() 32 33 return func() { 34 close(done) 35 } 36 } 37 38 // TestEtcdElector tests that two candidates competing for leadership works as 39 // expected and that elected leader can resign and allow others to take on. 40 func TestEtcdElector(t *testing.T) { 41 guard := GuardTimeout(t, 5*time.Second) 42 defer guard() 43 44 tmpDir, err := ioutil.TempDir("", "etcd") 45 if err != nil { 46 t.Fatalf("unable to create temp dir: %v", err) 47 } 48 49 etcdCfg, cleanup, err := etcd.NewEmbeddedEtcdInstance(tmpDir, 0, 0, "") 50 require.NoError(t, err) 51 defer cleanup() 52 53 ctx, cancel := context.WithCancel(context.Background()) 54 defer cancel() 55 56 const ( 57 election = "/election/" 58 id1 = "e1" 59 id2 = "e2" 60 ttl = 5 61 ) 62 63 e1, err := newEtcdLeaderElector( 64 ctx, id1, election, ttl, etcdCfg, 65 ) 66 require.NoError(t, err) 67 68 e2, err := newEtcdLeaderElector( 69 ctx, id2, election, ttl, etcdCfg, 70 ) 71 require.NoError(t, err) 72 73 var wg sync.WaitGroup 74 ch := make(chan *etcdLeaderElector) 75 76 wg.Add(2) 77 ctxb := context.Background() 78 79 go func() { 80 defer wg.Done() 81 require.NoError(t, e1.Campaign(ctxb)) 82 ch <- e1 83 }() 84 85 go func() { 86 defer wg.Done() 87 require.NoError(t, e2.Campaign(ctxb)) 88 ch <- e2 89 }() 90 91 tmp := <-ch 92 first, err := tmp.Leader(ctxb) 93 require.NoError(t, err) 94 require.NoError(t, tmp.Resign()) 95 96 tmp = <-ch 97 second, err := tmp.Leader(ctxb) 98 require.NoError(t, err) 99 require.NoError(t, tmp.Resign()) 100 101 require.Contains(t, []string{id1, id2}, first) 102 require.Contains(t, []string{id1, id2}, second) 103 require.NotEqual(t, first, second) 104 105 wg.Wait() 106 }