1// Copyright 2016 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 15package e2e 16 17import ( 18 "os" 19 "strings" 20 "testing" 21 "time" 22 23 "go.etcd.io/etcd/pkg/expect" 24) 25 26func TestCtlV3Elect(t *testing.T) { 27 oldenv := os.Getenv("EXPECT_DEBUG") 28 defer os.Setenv("EXPECT_DEBUG", oldenv) 29 os.Setenv("EXPECT_DEBUG", "1") 30 31 testCtl(t, testElect) 32} 33 34func testElect(cx ctlCtx) { 35 name := "a" 36 37 holder, ch, err := ctlV3Elect(cx, name, "p1") 38 if err != nil { 39 cx.t.Fatal(err) 40 } 41 42 l1 := "" 43 select { 44 case <-time.After(2 * time.Second): 45 cx.t.Fatalf("timed out electing") 46 case l1 = <-ch: 47 if !strings.HasPrefix(l1, name) { 48 cx.t.Errorf("got %q, expected %q prefix", l1, name) 49 } 50 } 51 52 // blocked process that won't win the election 53 blocked, ch, err := ctlV3Elect(cx, name, "p2") 54 if err != nil { 55 cx.t.Fatal(err) 56 } 57 select { 58 case <-time.After(100 * time.Millisecond): 59 case <-ch: 60 cx.t.Fatalf("should block") 61 } 62 63 // overlap with a blocker that will win the election 64 blockAcquire, ch, err := ctlV3Elect(cx, name, "p2") 65 if err != nil { 66 cx.t.Fatal(err) 67 } 68 defer blockAcquire.Stop() 69 select { 70 case <-time.After(100 * time.Millisecond): 71 case <-ch: 72 cx.t.Fatalf("should block") 73 } 74 75 // kill blocked process with clean shutdown 76 if err = blocked.Signal(os.Interrupt); err != nil { 77 cx.t.Fatal(err) 78 } 79 if err = closeWithTimeout(blocked, time.Second); err != nil { 80 cx.t.Fatal(err) 81 } 82 83 // kill the holder with clean shutdown 84 if err = holder.Signal(os.Interrupt); err != nil { 85 cx.t.Fatal(err) 86 } 87 if err = closeWithTimeout(holder, time.Second); err != nil { 88 cx.t.Fatal(err) 89 } 90 91 // blockAcquire should win the election 92 select { 93 case <-time.After(time.Second): 94 cx.t.Fatalf("timed out from waiting to holding") 95 case l2 := <-ch: 96 if l1 == l2 || !strings.HasPrefix(l2, name) { 97 cx.t.Fatalf("expected different elect name, got l1=%q, l2=%q", l1, l2) 98 } 99 } 100} 101 102// ctlV3Elect creates a elect process with a channel listening for when it wins the election. 103func ctlV3Elect(cx ctlCtx, name, proposal string) (*expect.ExpectProcess, <-chan string, error) { 104 cmdArgs := append(cx.PrefixArgs(), "elect", name, proposal) 105 proc, err := spawnCmd(cmdArgs) 106 outc := make(chan string, 1) 107 if err != nil { 108 close(outc) 109 return proc, outc, err 110 } 111 go func() { 112 s, xerr := proc.ExpectFunc(func(string) bool { return true }) 113 if xerr != nil { 114 cx.t.Errorf("expect failed (%v)", xerr) 115 } 116 outc <- s 117 }() 118 return proc, outc, err 119} 120