mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
276 lines
8.0 KiB
Go
276 lines
8.0 KiB
Go
// Copyright 2023 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 apply
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
pb "go.etcd.io/etcd/api/v3/etcdserverpb"
|
|
"go.etcd.io/etcd/server/v3/auth"
|
|
"go.etcd.io/etcd/server/v3/etcdserver/api/membership"
|
|
"go.etcd.io/etcd/server/v3/etcdserver/api/v3alarm"
|
|
"go.etcd.io/etcd/server/v3/etcdserver/cindex"
|
|
"go.etcd.io/etcd/server/v3/etcdserver/errors"
|
|
"go.etcd.io/etcd/server/v3/lease"
|
|
betesting "go.etcd.io/etcd/server/v3/storage/backend/testing"
|
|
"go.etcd.io/etcd/server/v3/storage/mvcc"
|
|
"go.etcd.io/etcd/server/v3/storage/schema"
|
|
)
|
|
|
|
const memberID = 111195
|
|
|
|
func defaultUberApplier(t *testing.T) UberApplier {
|
|
lg := zaptest.NewLogger(t)
|
|
be, _ := betesting.NewDefaultTmpBackend(t)
|
|
t.Cleanup(func() {
|
|
betesting.Close(t, be)
|
|
})
|
|
|
|
cluster := membership.NewCluster(lg)
|
|
cluster.AddMember(&membership.Member{ID: memberID}, true)
|
|
lessor := lease.NewLessor(lg, be, cluster, lease.LessorConfig{})
|
|
kv := mvcc.NewStore(lg, be, lessor, mvcc.StoreConfig{})
|
|
alarmStore, err := v3alarm.NewAlarmStore(lg, schema.NewAlarmBackend(lg, be))
|
|
require.NoError(t, err)
|
|
|
|
tp, err := auth.NewTokenProvider(lg, "simple", dummyIndexWaiter, 300*time.Second)
|
|
require.NoError(t, err)
|
|
authStore := auth.NewAuthStore(
|
|
lg,
|
|
schema.NewAuthBackend(lg, be),
|
|
tp,
|
|
bcrypt.DefaultCost,
|
|
)
|
|
consistentIndex := cindex.NewConsistentIndex(be)
|
|
return NewUberApplier(
|
|
lg,
|
|
be,
|
|
kv,
|
|
alarmStore,
|
|
authStore,
|
|
lessor,
|
|
cluster,
|
|
&fakeRaftStatusGetter{},
|
|
&fakeSnapshotServer{},
|
|
consistentIndex,
|
|
1*time.Hour,
|
|
false,
|
|
16*1024*1024, //16MB
|
|
)
|
|
}
|
|
|
|
// TestUberApplier_Alarm_Corrupt tests the applier returns ErrCorrupt after alarm CORRUPT is activated
|
|
func TestUberApplier_Alarm_Corrupt(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
request *pb.InternalRaftRequest
|
|
expectError error
|
|
}{
|
|
{
|
|
name: "Put request returns ErrCorrupt after alarm CORRUPT is activated",
|
|
request: &pb.InternalRaftRequest{Put: &pb.PutRequest{}},
|
|
expectError: errors.ErrCorrupt,
|
|
},
|
|
{
|
|
name: "Range request returns ErrCorrupt after alarm CORRUPT is activated",
|
|
request: &pb.InternalRaftRequest{Range: &pb.RangeRequest{}},
|
|
expectError: errors.ErrCorrupt,
|
|
},
|
|
{
|
|
name: "DeleteRange request returns ErrCorrupt after alarm CORRUPT is activated",
|
|
request: &pb.InternalRaftRequest{DeleteRange: &pb.DeleteRangeRequest{}},
|
|
expectError: errors.ErrCorrupt,
|
|
},
|
|
{
|
|
name: "Txn request returns ErrCorrupt after alarm CORRUPT is activated",
|
|
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{}},
|
|
expectError: errors.ErrCorrupt,
|
|
},
|
|
{
|
|
name: "Compaction request returns ErrCorrupt after alarm CORRUPT is activated",
|
|
request: &pb.InternalRaftRequest{Compaction: &pb.CompactionRequest{}},
|
|
expectError: errors.ErrCorrupt,
|
|
},
|
|
{
|
|
name: "LeaseGrant request returns ErrCorrupt after alarm CORRUPT is activated",
|
|
request: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}},
|
|
expectError: errors.ErrCorrupt,
|
|
},
|
|
{
|
|
name: "LeaseRevoke request returns ErrCorrupt after alarm CORRUPT is activated",
|
|
request: &pb.InternalRaftRequest{LeaseRevoke: &pb.LeaseRevokeRequest{}},
|
|
expectError: errors.ErrCorrupt,
|
|
},
|
|
}
|
|
|
|
ua := defaultUberApplier(t)
|
|
result := ua.Apply(&pb.InternalRaftRequest{
|
|
Header: &pb.RequestHeader{},
|
|
Alarm: &pb.AlarmRequest{
|
|
Action: pb.AlarmRequest_ACTIVATE,
|
|
MemberID: memberID,
|
|
Alarm: pb.AlarmType_CORRUPT,
|
|
},
|
|
})
|
|
require.NotNil(t, result)
|
|
require.Nil(t, result.Err)
|
|
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result = ua.Apply(tc.request)
|
|
require.NotNil(t, result)
|
|
require.Equalf(t, tc.expectError, result.Err, "Apply: got %v, expect: %v", result.Err, tc.expectError)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestUberApplier_Alarm_Quota tests the applier returns ErrNoSpace after alarm NOSPACE is activated
|
|
func TestUberApplier_Alarm_Quota(t *testing.T) {
|
|
tcs := []struct {
|
|
name string
|
|
request *pb.InternalRaftRequest
|
|
expectError error
|
|
}{
|
|
{
|
|
name: "Put request returns ErrCorrupt after alarm NOSPACE is activated",
|
|
request: &pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}},
|
|
expectError: errors.ErrNoSpace,
|
|
},
|
|
{
|
|
name: "Txn request cost > 0 returns ErrCorrupt after alarm NOSPACE is activated",
|
|
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
|
|
Success: []*pb.RequestOp{
|
|
{
|
|
Request: &pb.RequestOp_RequestPut{
|
|
RequestPut: &pb.PutRequest{
|
|
Key: []byte(key),
|
|
},
|
|
},
|
|
},
|
|
}}},
|
|
expectError: errors.ErrNoSpace,
|
|
},
|
|
{
|
|
name: "Txn request cost = 0 is still allowed after alarm NOSPACE is activated",
|
|
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
|
|
Success: []*pb.RequestOp{
|
|
{
|
|
Request: &pb.RequestOp_RequestRange{
|
|
RequestRange: &pb.RangeRequest{
|
|
Key: []byte(key),
|
|
},
|
|
},
|
|
},
|
|
}}},
|
|
expectError: nil,
|
|
},
|
|
{
|
|
name: "Txn request cost = 0 in both branches is still allowed after alarm NOSPACE is activated",
|
|
request: &pb.InternalRaftRequest{Txn: &pb.TxnRequest{
|
|
Compare: []*pb.Compare{
|
|
{
|
|
Key: []byte(key),
|
|
Result: pb.Compare_EQUAL,
|
|
Target: pb.Compare_CREATE,
|
|
TargetUnion: &pb.Compare_CreateRevision{CreateRevision: 0},
|
|
},
|
|
},
|
|
Success: []*pb.RequestOp{
|
|
{
|
|
Request: &pb.RequestOp_RequestRange{
|
|
RequestRange: &pb.RangeRequest{
|
|
Key: []byte(key),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Failure: []*pb.RequestOp{
|
|
{
|
|
Request: &pb.RequestOp_RequestDeleteRange{
|
|
RequestDeleteRange: &pb.DeleteRangeRequest{
|
|
Key: []byte(key),
|
|
},
|
|
},
|
|
},
|
|
}}},
|
|
expectError: nil,
|
|
},
|
|
{
|
|
name: "LeaseGrant request returns ErrCorrupt after alarm NOSPACE is activated",
|
|
request: &pb.InternalRaftRequest{LeaseGrant: &pb.LeaseGrantRequest{}},
|
|
expectError: errors.ErrNoSpace,
|
|
},
|
|
}
|
|
|
|
ua := defaultUberApplier(t)
|
|
result := ua.Apply(&pb.InternalRaftRequest{
|
|
Header: &pb.RequestHeader{},
|
|
Alarm: &pb.AlarmRequest{
|
|
Action: pb.AlarmRequest_ACTIVATE,
|
|
MemberID: memberID,
|
|
Alarm: pb.AlarmType_NOSPACE,
|
|
},
|
|
})
|
|
require.NotNil(t, result)
|
|
require.Nil(t, result.Err)
|
|
|
|
for _, tc := range tcs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result = ua.Apply(tc.request)
|
|
require.NotNil(t, result)
|
|
require.Equalf(t, tc.expectError, result.Err, "Apply: got %v, expect: %v", result.Err, tc.expectError)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestUberApplier_Alarm_Deactivate tests the applier should be able to apply after alarm is deactivated
|
|
func TestUberApplier_Alarm_Deactivate(t *testing.T) {
|
|
ua := defaultUberApplier(t)
|
|
result := ua.Apply(&pb.InternalRaftRequest{
|
|
Header: &pb.RequestHeader{},
|
|
Alarm: &pb.AlarmRequest{
|
|
Action: pb.AlarmRequest_ACTIVATE,
|
|
MemberID: memberID,
|
|
Alarm: pb.AlarmType_NOSPACE,
|
|
},
|
|
})
|
|
require.NotNil(t, result)
|
|
require.Nil(t, result.Err)
|
|
|
|
result = ua.Apply(&pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}})
|
|
require.NotNil(t, result)
|
|
require.Equalf(t, errors.ErrNoSpace, result.Err, "Apply: got %v, expect: %v", result.Err, errors.ErrNoSpace)
|
|
|
|
result = ua.Apply(&pb.InternalRaftRequest{
|
|
Header: &pb.RequestHeader{},
|
|
Alarm: &pb.AlarmRequest{
|
|
Action: pb.AlarmRequest_DEACTIVATE,
|
|
MemberID: memberID,
|
|
Alarm: pb.AlarmType_NOSPACE,
|
|
},
|
|
})
|
|
require.NotNil(t, result)
|
|
require.Nil(t, result.Err)
|
|
|
|
result = ua.Apply(&pb.InternalRaftRequest{Put: &pb.PutRequest{Key: []byte(key)}})
|
|
require.NotNil(t, result)
|
|
require.Nil(t, result.Err)
|
|
}
|