Merge branch 'main' into tests/ctl-alarm

# Conflicts:
#	tests/framework/e2e/etcdctl.go
#	tests/framework/interface.go
This commit is contained in:
nic-chen 2022-03-19 16:36:15 +08:00
commit 02516ab266
12 changed files with 521 additions and 227 deletions

View File

@ -31,14 +31,14 @@ func TestAlarm(t *testing.T) {
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
// test small put still works
smallbuf := strings.Repeat("a", 64)
if err := clus.Client().Put("1st_test", smallbuf); err != nil {
if err := clus.Client().Put("1st_test", smallbuf, config.PutOptions{}); err != nil {
t.Fatalf("alarmTest: put kv error (%v)", err)
}
// write some chunks to fill up the database
buf := strings.Repeat("b", os.Getpagesize())
for {
if err := clus.Client().Put("2nd_test", buf); err != nil {
if err := clus.Client().Put("2nd_test", buf, config.PutOptions{}); err != nil {
if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") {
t.Fatal(err)
}
@ -58,7 +58,7 @@ func TestAlarm(t *testing.T) {
}
// check that Put is rejected when alarm is on
if err := clus.Client().Put("3rd_test", smallbuf); err != nil {
if err := clus.Client().Put("3rd_test", smallbuf, config.PutOptions{}); err != nil {
if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") {
t.Fatal(err)
}
@ -94,7 +94,7 @@ func TestAlarm(t *testing.T) {
}
// put one more key below quota
if err := clus.Client().Put("4th_test", smallbuf); err != nil {
if err := clus.Client().Put("4th_test", smallbuf, config.PutOptions{}); err != nil {
t.Fatal(err)
}
})

View File

@ -47,7 +47,7 @@ func TestCompact(t *testing.T) {
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
var kvs = []testutils.KV{{Key: "key", Val: "val1"}, {Key: "key", Val: "val2"}, {Key: "key", Val: "val3"}}
for i := range kvs {
if err := clus.Client().Put(kvs[i].Key, kvs[i].Val); err != nil {
if err := clus.Client().Put(kvs[i].Key, kvs[i].Val, config.PutOptions{}); err != nil {
t.Fatalf("compactTest #%d: put kv error (%v)", i, err)
}
}

View File

@ -30,7 +30,7 @@ func TestDefragOnline(t *testing.T) {
defer clus.Close()
var kvs = []testutils.KV{{Key: "key", Val: "val1"}, {Key: "key", Val: "val2"}, {Key: "key", Val: "val3"}}
for i := range kvs {
if err := clus.Client().Put(kvs[i].Key, kvs[i].Val); err != nil {
if err := clus.Client().Put(kvs[i].Key, kvs[i].Val, config.PutOptions{}); err != nil {
t.Fatalf("compactTest #%d: put kv error (%v)", i, err)
}
}

View File

@ -60,7 +60,7 @@ func TestKVPut(t *testing.T) {
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
key, value := "foo", "bar"
if err := cc.Put(key, value); err != nil {
if err := cc.Put(key, value, config.PutOptions{}); err != nil {
t.Fatalf("count not put key %q, err: %s", key, err)
}
resp, err := cc.Get(key, config.GetOptions{Serializable: true})
@ -123,7 +123,7 @@ func TestKVGet(t *testing.T) {
)
for i := range kvs {
if err := cc.Put(kvs[i], "bar"); err != nil {
if err := cc.Put(kvs[i], "bar", config.PutOptions{}); err != nil {
t.Fatalf("count not put key %q, err: %s", kvs[i], err)
}
}
@ -246,7 +246,7 @@ func TestKVDelete(t *testing.T) {
}
for _, tt := range tests {
for i := range kvs {
if err := cc.Put(kvs[i], "bar"); err != nil {
if err := cc.Put(kvs[i], "bar", config.PutOptions{}); err != nil {
t.Fatalf("count not put key %q, err: %s", kvs[i], err)
}
}

322
tests/common/lease_test.go Normal file
View File

@ -0,0 +1,322 @@
// Copyright 2022 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package common
import (
"testing"
"time"
"github.com/stretchr/testify/require"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)
func TestLeaseGrantTimeToLive(t *testing.T) {
testRunner.BeforeTest(t)
tcs := []struct {
name string
config config.ClusterConfig
}{
{
name: "NoTLS",
config: config.ClusterConfig{ClusterSize: 1},
},
{
name: "PeerTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
},
{
name: "PeerAutoTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
},
{
name: "ClientTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
},
{
name: "ClientAutoTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
clus := testRunner.NewCluster(t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
ttl := int64(10)
leaseResp, err := cc.Grant(ttl)
require.NoError(t, err)
ttlResp, err := cc.TimeToLive(leaseResp.ID, config.LeaseOption{})
require.NoError(t, err)
require.Equal(t, ttl, ttlResp.GrantedTTL)
})
})
}
}
func TestLeaseGrantAndList(t *testing.T) {
testRunner.BeforeTest(t)
tcs := []struct {
name string
config config.ClusterConfig
}{
{
name: "NoTLS",
config: config.ClusterConfig{ClusterSize: 1},
},
{
name: "PeerTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
},
{
name: "PeerAutoTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
},
{
name: "ClientTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
},
{
name: "ClientAutoTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
},
}
for _, tc := range tcs {
nestedCases := []struct {
name string
leaseCount int
}{
{
name: "no_leases",
leaseCount: 0,
},
{
name: "one_lease",
leaseCount: 1,
},
{
name: "many_leases",
leaseCount: 3,
},
}
for _, nc := range nestedCases {
t.Run(tc.name+"/"+nc.name, func(t *testing.T) {
clus := testRunner.NewCluster(t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
createdLeases := []clientv3.LeaseID{}
for i := 0; i < nc.leaseCount; i++ {
leaseResp, err := cc.Grant(10)
require.NoError(t, err)
createdLeases = append(createdLeases, leaseResp.ID)
}
resp, err := cc.LeaseList()
require.NoError(t, err)
require.Len(t, resp.Leases, nc.leaseCount)
returnedLeases := make([]clientv3.LeaseID, 0, nc.leaseCount)
for _, status := range resp.Leases {
returnedLeases = append(returnedLeases, status.ID)
}
require.ElementsMatch(t, createdLeases, returnedLeases)
})
})
}
}
}
func TestLeaseGrantTimeToLiveExpired(t *testing.T) {
testRunner.BeforeTest(t)
tcs := []struct {
name string
config config.ClusterConfig
}{
{
name: "NoTLS",
config: config.ClusterConfig{ClusterSize: 1},
},
{
name: "PeerTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
},
{
name: "PeerAutoTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
},
{
name: "ClientTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
},
{
name: "ClientAutoTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
clus := testRunner.NewCluster(t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
leaseResp, err := cc.Grant(2)
require.NoError(t, err)
err = cc.Put("foo", "bar", config.PutOptions{LeaseID: leaseResp.ID})
require.NoError(t, err)
getResp, err := cc.Get("foo", config.GetOptions{})
require.NoError(t, err)
require.Equal(t, int64(1), getResp.Count)
time.Sleep(3 * time.Second)
ttlResp, err := cc.TimeToLive(leaseResp.ID, config.LeaseOption{})
require.NoError(t, err)
require.Equal(t, int64(-1), ttlResp.TTL)
getResp, err = cc.Get("foo", config.GetOptions{})
require.NoError(t, err)
// Value should expire with the lease
require.Equal(t, int64(0), getResp.Count)
})
})
}
}
func TestLeaseGrantKeepAliveOnce(t *testing.T) {
testRunner.BeforeTest(t)
tcs := []struct {
name string
config config.ClusterConfig
}{
{
name: "NoTLS",
config: config.ClusterConfig{ClusterSize: 1},
},
{
name: "PeerTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
},
{
name: "PeerAutoTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
},
{
name: "ClientTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
},
{
name: "ClientAutoTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
clus := testRunner.NewCluster(t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
leaseResp, err := cc.Grant(2)
require.NoError(t, err)
_, err = cc.LeaseKeepAliveOnce(leaseResp.ID)
require.NoError(t, err)
time.Sleep(2 * time.Second) // Wait for the original lease to expire
ttlResp, err := cc.TimeToLive(leaseResp.ID, config.LeaseOption{})
require.NoError(t, err)
// We still have a lease!
require.Greater(t, int64(2), ttlResp.TTL)
})
})
}
}
func TestLeaseGrantRevoke(t *testing.T) {
testRunner.BeforeTest(t)
tcs := []struct {
name string
config config.ClusterConfig
}{
{
name: "NoTLS",
config: config.ClusterConfig{ClusterSize: 1},
},
{
name: "PeerTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.ManualTLS},
},
{
name: "PeerAutoTLS",
config: config.ClusterConfig{ClusterSize: 3, PeerTLS: config.AutoTLS},
},
{
name: "ClientTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.ManualTLS},
},
{
name: "ClientAutoTLS",
config: config.ClusterConfig{ClusterSize: 1, ClientTLS: config.AutoTLS},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
clus := testRunner.NewCluster(t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteWithTimeout(t, 10*time.Second, func() {
leaseResp, err := cc.Grant(20)
require.NoError(t, err)
err = cc.Put("foo", "bar", config.PutOptions{LeaseID: leaseResp.ID})
require.NoError(t, err)
getResp, err := cc.Get("foo", config.GetOptions{})
require.NoError(t, err)
require.Equal(t, int64(1), getResp.Count)
_, err = cc.LeaseRevoke(leaseResp.ID)
require.NoError(t, err)
ttlResp, err := cc.TimeToLive(leaseResp.ID, config.LeaseOption{})
require.NoError(t, err)
require.Equal(t, int64(-1), ttlResp.TTL)
getResp, err = cc.Get("foo", config.GetOptions{})
require.NoError(t, err)
// Value should expire with the lease
require.Equal(t, int64(0), getResp.Count)
})
})
}
}

View File

@ -853,6 +853,28 @@ func authLeaseTestTimeToLiveExpired(cx ctlCtx) {
}
}
func leaseTestTimeToLiveExpire(cx ctlCtx, ttl int) error {
leaseID, err := ctlV3LeaseGrant(cx, ttl)
if err != nil {
return fmt.Errorf("ctlV3LeaseGrant error (%v)", err)
}
if err = ctlV3Put(cx, "key", "val", leaseID); err != nil {
return fmt.Errorf("ctlV3Put error (%v)", err)
}
// eliminate false positive
time.Sleep(time.Duration(ttl+1) * time.Second)
cmdArgs := append(cx.PrefixArgs(), "lease", "timetolive", leaseID)
exp := fmt.Sprintf("lease %s already expired", leaseID)
if err = e2e.SpawnWithExpectWithEnv(cmdArgs, cx.envMap, exp); err != nil {
return fmt.Errorf("lease not properly expired: (%v)", err)
}
if err := ctlV3Get(cx, []string{"key"}); err != nil {
return fmt.Errorf("ctlV3Get error (%v)", err)
}
return nil
}
func authLeaseTestLeaseGrantLeases(cx ctlCtx) {
cx.user, cx.pass = "root", "root"
authSetupTestUser(cx)
@ -862,6 +884,24 @@ func authLeaseTestLeaseGrantLeases(cx ctlCtx) {
}
}
func leaseTestGrantLeasesList(cx ctlCtx) error {
id, err := ctlV3LeaseGrant(cx, 10)
if err != nil {
return fmt.Errorf("ctlV3LeaseGrant error (%v)", err)
}
cmdArgs := append(cx.PrefixArgs(), "lease", "list")
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
if err != nil {
return fmt.Errorf("lease list failed (%v)", err)
}
_, err = proc.Expect(id)
if err != nil {
return fmt.Errorf("lease id not in returned list (%v)", err)
}
return proc.Close()
}
func authLeaseTestLeaseRevoke(cx ctlCtx) {
cx.user, cx.pass = "root", "root"
authSetupTestUser(cx)

View File

@ -24,6 +24,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"go.etcd.io/etcd/tests/v3/framework/config"
"go.etcd.io/etcd/tests/v3/framework/e2e"
"go.etcd.io/etcd/tests/v3/framework/testutils"
)
@ -100,7 +101,7 @@ func TestAuthority(t *testing.T) {
endpoints := templateEndpoints(t, tc.clientURLPattern, epc)
client := e2e.NewEtcdctl(cfg, endpoints)
err = client.Put("foo", "bar")
err = client.Put("foo", "bar", config.PutOptions{})
if err != nil {
t.Fatal(err)
}

View File

@ -19,53 +19,10 @@ import (
"strconv"
"strings"
"testing"
"time"
"go.etcd.io/etcd/tests/v3/framework/e2e"
)
func TestCtlV3LeaseGrantTimeToLive(t *testing.T) { testCtl(t, leaseTestGrantTimeToLive) }
func TestCtlV3LeaseGrantTimeToLiveNoTLS(t *testing.T) {
testCtl(t, leaseTestGrantTimeToLive, withCfg(*e2e.NewConfigNoTLS()))
}
func TestCtlV3LeaseGrantTimeToLiveClientTLS(t *testing.T) {
testCtl(t, leaseTestGrantTimeToLive, withCfg(*e2e.NewConfigClientTLS()))
}
func TestCtlV3LeaseGrantTimeToLiveClientAutoTLS(t *testing.T) {
testCtl(t, leaseTestGrantTimeToLive, withCfg(*e2e.NewConfigClientAutoTLS()))
}
func TestCtlV3LeaseGrantTimeToLivePeerTLS(t *testing.T) {
testCtl(t, leaseTestGrantTimeToLive, withCfg(*e2e.NewConfigPeerTLS()))
}
func TestCtlV3LeaseGrantLeases(t *testing.T) { testCtl(t, leaseTestGrantLeaseListed) }
func TestCtlV3LeaseGrantLeasesNoTLS(t *testing.T) {
testCtl(t, leaseTestGrantLeaseListed, withCfg(*e2e.NewConfigNoTLS()))
}
func TestCtlV3LeaseGrantLeasesClientTLS(t *testing.T) {
testCtl(t, leaseTestGrantLeaseListed, withCfg(*e2e.NewConfigClientTLS()))
}
func TestCtlV3LeaseGrantLeasesClientAutoTLS(t *testing.T) {
testCtl(t, leaseTestGrantLeaseListed, withCfg(*e2e.NewConfigClientAutoTLS()))
}
func TestCtlV3LeaseGrantLeasesPeerTLS(t *testing.T) {
testCtl(t, leaseTestGrantLeaseListed, withCfg(*e2e.NewConfigPeerTLS()))
}
func TestCtlV3LeaseTestTimeToLiveExpired(t *testing.T) { testCtl(t, leaseTestTimeToLiveExpired) }
func TestCtlV3LeaseTestTimeToLiveExpiredNoTLS(t *testing.T) {
testCtl(t, leaseTestTimeToLiveExpired, withCfg(*e2e.NewConfigNoTLS()))
}
func TestCtlV3LeaseTestTimeToLiveExpiredClientTLS(t *testing.T) {
testCtl(t, leaseTestTimeToLiveExpired, withCfg(*e2e.NewConfigClientTLS()))
}
func TestCtlV3LeaseTestTimeToLiveExpiredClientAutoTLS(t *testing.T) {
testCtl(t, leaseTestTimeToLiveExpired, withCfg(*e2e.NewConfigClientAutoTLS()))
}
func TestCtlV3LeaseTestTimeToLiveExpiredPeerTLS(t *testing.T) {
testCtl(t, leaseTestTimeToLiveExpired, withCfg(*e2e.NewConfigPeerTLS()))
}
func TestCtlV3LeaseKeepAlive(t *testing.T) { testCtl(t, leaseTestKeepAlive) }
func TestCtlV3LeaseKeepAliveNoTLS(t *testing.T) {
testCtl(t, leaseTestKeepAlive, withCfg(*e2e.NewConfigNoTLS()))
@ -80,114 +37,6 @@ func TestCtlV3LeaseKeepAlivePeerTLS(t *testing.T) {
testCtl(t, leaseTestKeepAlive, withCfg(*e2e.NewConfigPeerTLS()))
}
func TestCtlV3LeaseKeepAliveOnce(t *testing.T) { testCtl(t, leaseTestKeepAliveOnce) }
func TestCtlV3LeaseKeepAliveOnceNoTLS(t *testing.T) {
testCtl(t, leaseTestKeepAliveOnce, withCfg(*e2e.NewConfigNoTLS()))
}
func TestCtlV3LeaseKeepAliveOnceClientTLS(t *testing.T) {
testCtl(t, leaseTestKeepAliveOnce, withCfg(*e2e.NewConfigClientTLS()))
}
func TestCtlV3LeaseKeepAliveOnceClientAutoTLS(t *testing.T) {
testCtl(t, leaseTestKeepAliveOnce, withCfg(*e2e.NewConfigClientAutoTLS()))
}
func TestCtlV3LeaseKeepAliveOncePeerTLS(t *testing.T) {
testCtl(t, leaseTestKeepAliveOnce, withCfg(*e2e.NewConfigPeerTLS()))
}
func TestCtlV3LeaseRevoke(t *testing.T) { testCtl(t, leaseTestRevoked) }
func TestCtlV3LeaseRevokeNoTLS(t *testing.T) {
testCtl(t, leaseTestRevoked, withCfg(*e2e.NewConfigNoTLS()))
}
func TestCtlV3LeaseRevokeClientTLS(t *testing.T) {
testCtl(t, leaseTestRevoked, withCfg(*e2e.NewConfigClientTLS()))
}
func TestCtlV3LeaseRevokeClientAutoTLS(t *testing.T) {
testCtl(t, leaseTestRevoked, withCfg(*e2e.NewConfigClientAutoTLS()))
}
func TestCtlV3LeaseRevokePeerTLS(t *testing.T) {
testCtl(t, leaseTestRevoked, withCfg(*e2e.NewConfigPeerTLS()))
}
func leaseTestGrantTimeToLive(cx ctlCtx) {
id, err := ctlV3LeaseGrant(cx, 10)
if err != nil {
cx.t.Fatalf("leaseTestGrantTimeToLive: ctlV3LeaseGrant error (%v)", err)
}
cmdArgs := append(cx.PrefixArgs(), "lease", "timetolive", id, "--keys")
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
if err != nil {
cx.t.Fatalf("leaseTestGrantTimeToLive: error (%v)", err)
}
line, err := proc.Expect(" granted with TTL(")
if err != nil {
cx.t.Fatalf("leaseTestGrantTimeToLive: error (%v)", err)
}
if err = proc.Close(); err != nil {
cx.t.Fatalf("leaseTestGrantTimeToLive: error (%v)", err)
}
if !strings.Contains(line, ", attached keys") {
cx.t.Fatalf("leaseTestGrantTimeToLive: expected 'attached keys', got %q", line)
}
if !strings.Contains(line, id) {
cx.t.Fatalf("leaseTestGrantTimeToLive: expected leaseID %q, got %q", id, line)
}
}
func leaseTestGrantLeaseListed(cx ctlCtx) {
err := leaseTestGrantLeasesList(cx)
if err != nil {
cx.t.Fatalf("leaseTestGrantLeasesList: (%v)", err)
}
}
func leaseTestGrantLeasesList(cx ctlCtx) error {
id, err := ctlV3LeaseGrant(cx, 10)
if err != nil {
return fmt.Errorf("ctlV3LeaseGrant error (%v)", err)
}
cmdArgs := append(cx.PrefixArgs(), "lease", "list")
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
if err != nil {
return fmt.Errorf("lease list failed (%v)", err)
}
_, err = proc.Expect(id)
if err != nil {
return fmt.Errorf("lease id not in returned list (%v)", err)
}
return proc.Close()
}
func leaseTestTimeToLiveExpired(cx ctlCtx) {
err := leaseTestTimeToLiveExpire(cx, 3)
if err != nil {
cx.t.Fatalf("leaseTestTimeToLiveExpire: (%v)", err)
}
}
func leaseTestTimeToLiveExpire(cx ctlCtx, ttl int) error {
leaseID, err := ctlV3LeaseGrant(cx, ttl)
if err != nil {
return fmt.Errorf("ctlV3LeaseGrant error (%v)", err)
}
if err = ctlV3Put(cx, "key", "val", leaseID); err != nil {
return fmt.Errorf("ctlV3Put error (%v)", err)
}
// eliminate false positive
time.Sleep(time.Duration(ttl+1) * time.Second)
cmdArgs := append(cx.PrefixArgs(), "lease", "timetolive", leaseID)
exp := fmt.Sprintf("lease %s already expired", leaseID)
if err = e2e.SpawnWithExpectWithEnv(cmdArgs, cx.envMap, exp); err != nil {
return fmt.Errorf("lease not properly expired: (%v)", err)
}
if err := ctlV3Get(cx, []string{"key"}); err != nil {
return fmt.Errorf("ctlV3Get error (%v)", err)
}
return nil
}
func leaseTestKeepAlive(cx ctlCtx) {
// put with TTL 10 seconds and keep-alive
leaseID, err := ctlV3LeaseGrant(cx, 10)
@ -205,48 +54,6 @@ func leaseTestKeepAlive(cx ctlCtx) {
}
}
func leaseTestKeepAliveOnce(cx ctlCtx) {
// put with TTL 10 seconds and keep-alive once
leaseID, err := ctlV3LeaseGrant(cx, 10)
if err != nil {
cx.t.Fatalf("leaseTestKeepAlive: ctlV3LeaseGrant error (%v)", err)
}
if err := ctlV3Put(cx, "key", "val", leaseID); err != nil {
cx.t.Fatalf("leaseTestKeepAlive: ctlV3Put error (%v)", err)
}
if err := ctlV3LeaseKeepAliveOnce(cx, leaseID); err != nil {
cx.t.Fatalf("leaseTestKeepAlive: ctlV3LeaseKeepAliveOnce error (%v)", err)
}
if err := ctlV3Get(cx, []string{"key"}, kv{"key", "val"}); err != nil {
cx.t.Fatalf("leaseTestKeepAlive: ctlV3Get error (%v)", err)
}
}
func leaseTestRevoked(cx ctlCtx) {
err := leaseTestRevoke(cx)
if err != nil {
cx.t.Fatalf("leaseTestRevoke: (%v)", err)
}
}
func leaseTestRevoke(cx ctlCtx) error {
// put with TTL 10 seconds and revoke
leaseID, err := ctlV3LeaseGrant(cx, 10)
if err != nil {
return fmt.Errorf("ctlV3LeaseGrant error (%v)", err)
}
if err := ctlV3Put(cx, "key", "val", leaseID); err != nil {
return fmt.Errorf("ctlV3Put error (%v)", err)
}
if err := ctlV3LeaseRevoke(cx, leaseID); err != nil {
return fmt.Errorf("ctlV3LeaseRevoke error (%v)", err)
}
if err := ctlV3Get(cx, []string{"key"}); err != nil { // expect no output
return fmt.Errorf("ctlV3Get error (%v)", err)
}
return nil
}
func ctlV3LeaseGrant(cx ctlCtx, ttl int) (string, error) {
cmdArgs := append(cx.PrefixArgs(), "lease", "grant", strconv.Itoa(ttl))
proc, err := e2e.SpawnCmd(cmdArgs, cx.envMap)
@ -284,20 +91,6 @@ func ctlV3LeaseKeepAlive(cx ctlCtx, leaseID string) error {
return proc.Stop()
}
func ctlV3LeaseKeepAliveOnce(cx ctlCtx, leaseID string) error {
cmdArgs := append(cx.PrefixArgs(), "lease", "keep-alive", "--once", leaseID)
proc, err := e2e.SpawnCmd(cmdArgs, nil)
if err != nil {
return err
}
if _, err = proc.Expect(fmt.Sprintf("lease %s keepalived with TTL(", leaseID)); err != nil {
return err
}
return proc.Stop()
}
func ctlV3LeaseRevoke(cx ctlCtx, leaseID string) error {
cmdArgs := append(cx.PrefixArgs(), "lease", "revoke", leaseID)
return e2e.SpawnWithExpectWithEnv(cmdArgs, cx.envMap, fmt.Sprintf("lease %s revoked", leaseID))

View File

@ -32,6 +32,10 @@ type GetOptions struct {
SortBy clientv3.SortTarget
}
type PutOptions struct {
LeaseID clientv3.LeaseID
}
type DeleteOptions struct {
Prefix bool
FromKey bool
@ -46,3 +50,7 @@ type CompactOption struct {
type DefragOption struct {
Timeout time.Duration
}
type LeaseOption struct {
WithAttachedKeys bool
}

View File

@ -17,6 +17,7 @@ package e2e
import (
"encoding/json"
"fmt"
"strconv"
"strings"
clientv3 "go.etcd.io/etcd/client/v3"
@ -106,8 +107,13 @@ func (ctl *EtcdctlV3) Get(key string, o config.GetOptions) (*clientv3.GetRespons
return &resp, err
}
func (ctl *EtcdctlV3) Put(key, value string) error {
return SpawnWithExpect(ctl.cmdArgs("put", key, value), "OK")
func (ctl *EtcdctlV3) Put(key, value string, opts config.PutOptions) error {
args := ctl.cmdArgs()
args = append(args, "put", key, value)
if opts.LeaseID != 0 {
args = append(args, "--lease", strconv.FormatInt(int64(opts.LeaseID), 16))
}
return SpawnWithExpect(args, "OK")
}
func (ctl *EtcdctlV3) Delete(key string, o config.DeleteOptions) (*clientv3.DeleteResponse, error) {
@ -185,7 +191,6 @@ func (ctl *EtcdctlV3) Status() ([]*clientv3.StatusResponse, error) {
if err != nil {
return nil, err
}
var epStatus []*struct {
Endpoint string
Status *clientv3.StatusResponse
@ -243,15 +248,42 @@ func (ctl *EtcdctlV3) Health() error {
return SpawnWithExpects(args, map[string]string{}, lines...)
}
func (ctl *EtcdctlV3) Alarm(cmd string) (*clientv3.AlarmResponse, error) {
func (ctl *EtcdctlV3) Grant(ttl int64) (*clientv3.LeaseGrantResponse, error) {
args := ctl.cmdArgs()
args = append(args, "alarm", cmd)
args = append(args, "lease", "grant", strconv.FormatInt(ttl, 10), "-w", "json")
cmd, err := SpawnCmd(args, nil)
if err != nil {
return nil, err
}
var resp clientv3.LeaseGrantResponse
line, err := cmd.Expect("ID")
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}
return nil, SpawnWithExpect(args, "alarm:NOSPACE")
func (ctl *EtcdctlV3) TimeToLive(id clientv3.LeaseID, o config.LeaseOption) (*clientv3.LeaseTimeToLiveResponse, error) {
args := ctl.cmdArgs()
args = append(args, "lease", "timetolive", strconv.FormatInt(int64(id), 16), "-w", "json")
if o.WithAttachedKeys {
args = append(args, "--keys")
}
cmd, err := SpawnCmd(args, nil)
if err != nil {
return nil, err
}
var resp clientv3.LeaseTimeToLiveResponse
line, err := cmd.Expect("id")
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}
func (ctl *EtcdctlV3) Defragment(o config.DefragOption) error {
args := append(ctl.cmdArgs(), "defrag")
if o.Timeout != 0 {
args = append(args, fmt.Sprintf("--command-timeout=%s", o.Timeout))
@ -263,3 +295,58 @@ func (ctl *EtcdctlV3) Defragment(o config.DefragOption) error {
_, err := SpawnWithExpectLines(args, map[string]string{}, lines...)
return err
}
func (ctl *EtcdctlV3) LeaseList() (*clientv3.LeaseLeasesResponse, error) {
args := ctl.cmdArgs()
args = append(args, "lease", "list", "-w", "json")
cmd, err := SpawnCmd(args, nil)
if err != nil {
return nil, err
}
var resp clientv3.LeaseLeasesResponse
line, err := cmd.Expect("id")
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}
func (ctl *EtcdctlV3) LeaseKeepAliveOnce(id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error) {
args := ctl.cmdArgs()
args = append(args, "lease", "keep-alive", strconv.FormatInt(int64(id), 16), "--once", "-w", "json")
cmd, err := SpawnCmd(args, nil)
if err != nil {
return nil, err
}
var resp clientv3.LeaseKeepAliveResponse
line, err := cmd.Expect("ID")
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}
func (ctl *EtcdctlV3) LeaseRevoke(id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) {
args := ctl.cmdArgs()
args = append(args, "lease", "revoke", strconv.FormatInt(int64(id), 16), "-w", "json")
cmd, err := SpawnCmd(args, nil)
if err != nil {
return nil, err
}
var resp clientv3.LeaseRevokeResponse
line, err := cmd.Expect("header")
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(line), &resp)
return &resp, err
}
func (ctl *EtcdctlV3) Alarm(cmd string) (*clientv3.AlarmResponse, error) {
args := ctl.cmdArgs()
args = append(args, "alarm", cmd)
return nil, SpawnWithExpect(args, "alarm:NOSPACE")
}

View File

@ -127,8 +127,12 @@ func (c integrationClient) Get(key string, o config.GetOptions) (*clientv3.GetRe
return c.Client.Get(context.Background(), key, clientOpts...)
}
func (c integrationClient) Put(key, value string) error {
_, err := c.Client.Put(context.Background(), key, value)
func (c integrationClient) Put(key, value string, opts config.PutOptions) error {
clientOpts := []clientv3.OpOption{}
if opts.LeaseID != 0 {
clientOpts = append(clientOpts, clientv3.WithLease(opts.LeaseID))
}
_, err := c.Client.Put(context.Background(), key, value, clientOpts...)
return err
}
@ -221,3 +225,37 @@ func (c integrationClient) Defragment(o config.DefragOption) error {
}
return nil
}
func (c integrationClient) Grant(ttl int64) (*clientv3.LeaseGrantResponse, error) {
ctx := context.Background()
return c.Client.Grant(ctx, ttl)
}
func (c integrationClient) TimeToLive(id clientv3.LeaseID, o config.LeaseOption) (*clientv3.LeaseTimeToLiveResponse, error) {
ctx := context.Background()
leaseOpts := []clientv3.LeaseOption{}
if o.WithAttachedKeys {
leaseOpts = append(leaseOpts, clientv3.WithAttachedKeys())
}
return c.Client.TimeToLive(ctx, id, leaseOpts...)
}
func (c integrationClient) LeaseList() (*clientv3.LeaseLeasesResponse, error) {
ctx := context.Background()
return c.Client.Leases(ctx)
}
func (c integrationClient) LeaseKeepAliveOnce(id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error) {
ctx := context.Background()
return c.Client.KeepAliveOnce(ctx, id)
}
func (c integrationClient) LeaseRevoke(id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) {
ctx := context.Background()
return c.Client.Revoke(ctx, id)
}

View File

@ -33,7 +33,7 @@ type Cluster interface {
}
type Client interface {
Put(key, value string) error
Put(key, value string, opts config.PutOptions) error
Get(key string, opts config.GetOptions) (*clientv3.GetResponse, error)
Delete(key string, opts config.DeleteOptions) (*clientv3.DeleteResponse, error)
Compact(rev int64, opts config.CompactOption) (*clientv3.CompactResponse, error)
@ -42,4 +42,9 @@ type Client interface {
Health() error
Defragment(opts config.DefragOption) error
Alarm(cmd string) (*clientv3.AlarmResponse, error)
Grant(ttl int64) (*clientv3.LeaseGrantResponse, error)
TimeToLive(id clientv3.LeaseID, opts config.LeaseOption) (*clientv3.LeaseTimeToLiveResponse, error)
LeaseList() (*clientv3.LeaseLeasesResponse, error)
LeaseKeepAliveOnce(id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error)
LeaseRevoke(id clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error)
}