etcd/tests/common/lease_test.go
2022-05-16 14:35:44 +02:00

247 lines
6.7 KiB
Go

// 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 (
"context"
"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) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteUntil(ctx, t, 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)
for _, tc := range clusterTestCases {
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) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
t.Logf("Creating cluster...")
clus := testRunner.NewCluster(ctx, t, tc.config)
defer clus.Close()
cc := clus.Client()
t.Logf("Created cluster and client")
testutils.ExecuteUntil(ctx, t, func() {
createdLeases := []clientv3.LeaseID{}
for i := 0; i < nc.leaseCount; i++ {
leaseResp, err := cc.Grant(10)
t.Logf("Grant returned: resp:%s err:%v", leaseResp.String(), err)
require.NoError(t, err)
createdLeases = append(createdLeases, leaseResp.ID)
}
// Because we're not guarunteed to talk to the same member, wait for
// listing to eventually return true, either by the result propagaing
// or by hitting an up to date member.
leases := []clientv3.LeaseStatus{}
require.Eventually(t, func() bool {
resp, err := cc.LeaseList()
if err != nil {
return false
}
leases = resp.Leases
// TODO: update this to use last Revision from leaseResp
// after https://github.com/etcd-io/etcd/issues/13989 is fixed
return len(leases) == len(createdLeases)
}, 2*time.Second, 10*time.Millisecond)
returnedLeases := make([]clientv3.LeaseID, 0, nc.leaseCount)
for _, status := range leases {
returnedLeases = append(returnedLeases, status.ID)
}
require.ElementsMatch(t, createdLeases, returnedLeases)
})
})
}
}
}
func TestLeaseGrantTimeToLiveExpired(t *testing.T) {
testRunner.BeforeTest(t)
for _, tc := range clusterTestCases {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteUntil(ctx, t, 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)
for _, tc := range clusterTestCases {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteUntil(ctx, t, 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)
for _, tc := range clusterTestCases {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
clus := testRunner.NewCluster(ctx, t, tc.config)
defer clus.Close()
cc := clus.Client()
testutils.ExecuteUntil(ctx, t, 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)
})
})
}
}