go.etcd.io/etcd@v3.3.27+incompatible/clientv3/concurrency/election_test.go (about) 1 // Copyright 2018 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package concurrency_test 16 17 import ( 18 "context" 19 "log" 20 "strings" 21 "testing" 22 "time" 23 24 "github.com/coreos/etcd/clientv3" 25 "github.com/coreos/etcd/clientv3/concurrency" 26 ) 27 28 func TestResumeElection(t *testing.T) { 29 const prefix = "/resume-election/" 30 31 cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) 32 if err != nil { 33 log.Fatal(err) 34 } 35 defer cli.Close() 36 37 s, err := concurrency.NewSession(cli) 38 if err != nil { 39 log.Fatal(err) 40 } 41 defer s.Close() 42 43 e := concurrency.NewElection(s, prefix) 44 45 // Entire test should never take more than 10 seconds 46 ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) 47 defer cancel() 48 49 // Become leader 50 if err := e.Campaign(ctx, "candidate1"); err != nil { 51 t.Fatalf("Campaign() returned non nil err: %s", err) 52 } 53 54 // Get the leadership details of the current election 55 leader, err := e.Leader(ctx) 56 if err != nil { 57 t.Fatalf("Leader() returned non nil err: %s", err) 58 } 59 60 // Recreate the election 61 e = concurrency.ResumeElection(s, prefix, 62 string(leader.Kvs[0].Key), leader.Kvs[0].CreateRevision) 63 64 respChan := make(chan *clientv3.GetResponse) 65 go func() { 66 o := e.Observe(ctx) 67 respChan <- nil 68 for { 69 select { 70 case resp, ok := <-o: 71 if !ok { 72 t.Fatal("Observe() channel closed prematurely") 73 } 74 // Ignore any observations that candidate1 was elected 75 if string(resp.Kvs[0].Value) == "candidate1" { 76 continue 77 } 78 respChan <- &resp 79 return 80 } 81 } 82 }() 83 84 // Wait until observe goroutine is running 85 <-respChan 86 87 // Put some random data to generate a change event, this put should be 88 // ignored by Observe() because it is not under the election prefix. 89 _, err = cli.Put(ctx, "foo", "bar") 90 if err != nil { 91 t.Fatalf("Put('foo') returned non nil err: %s", err) 92 } 93 94 // Resign as leader 95 if err := e.Resign(ctx); err != nil { 96 t.Fatalf("Resign() returned non nil err: %s", err) 97 } 98 99 // Elect a different candidate 100 if err := e.Campaign(ctx, "candidate2"); err != nil { 101 t.Fatalf("Campaign() returned non nil err: %s", err) 102 } 103 104 // Wait for observed leader change 105 resp := <-respChan 106 107 kv := resp.Kvs[0] 108 if !strings.HasPrefix(string(kv.Key), prefix) { 109 t.Errorf("expected observed election to have prefix '%s' got '%s'", prefix, string(kv.Key)) 110 } 111 if string(kv.Value) != "candidate2" { 112 t.Errorf("expected new leader to be 'candidate1' got '%s'", string(kv.Value)) 113 } 114 }