integration: test require leader for a lease stream
@@ -20,6 +20,7 @@ import (
"time"
"golang.org/x/net/context"
+ "google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
@@ -490,6 +491,45 @@ func TestV3LeaseFailover(t *testing.T) {
}
}
+// TestV3LeaseRequireLeader ensures that a Recv will get a leader
+// loss error if there is no leader.
+func TestV3LeaseRequireLeader(t *testing.T) {
+ defer testutil.AfterTest(t)
+
+ clus := NewClusterV3(t, &ClusterConfig{Size: 3})
+ defer clus.Terminate(t)
+
+ lc := toGRPC(clus.Client(0)).Lease
+ clus.Members[1].Stop(t)
+ clus.Members[2].Stop(t)
+
+ md := metadata.Pairs(rpctypes.MetadataRequireLeaderKey, rpctypes.MetadataHasLeader)
+ mctx := metadata.NewContext(context.Background(), md)
+ ctx, cancel := context.WithCancel(mctx)
+ defer cancel()
+ lac, err := lc.LeaseKeepAlive(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ donec := make(chan struct{})
+ go func() {
+ defer close(donec)
+ resp, err := lac.Recv()
+ if err == nil {
+ t.Fatalf("got response %+v, expected error", resp)
+ }
+ if grpc.ErrorDesc(err) != rpctypes.ErrNoLeader.Error() {
+ t.Errorf("err = %v, want %v", err, rpctypes.ErrNoLeader)
+ }
+ }()
+ select {
+ case <-time.After(time.Duration(5*electionTicks) * tickDuration):
+ t.Fatalf("did not receive leader loss error")
+ case <-donec:
+ }
+}
+
const fiveMinTTL int64 = 300
// TestV3LeaseRecoverAndRevoke ensures that revoking a lease after restart deletes the attached key.
