diff --git a/server/auth/store.go b/server/auth/store.go index ed302638378..ee1a9012cbf 100644 --- a/server/auth/store.go +++ b/server/auth/store.go @@ -938,7 +938,7 @@ func (as *authStore) IsAuthEnabled() bool { } // NewAuthStore creates a new AuthStore. -func NewAuthStore(lg *zap.Logger, be AuthBackend, tp TokenProvider, bcryptCost int) *authStore { +func NewAuthStore(lg *zap.Logger, be AuthBackend, tp TokenProvider, bcryptCost int) AuthStore { if lg == nil { lg = zap.NewNop() } diff --git a/server/auth/store_test.go b/server/auth/store_test.go index c8cd5cad7cc..765ac1f62c6 100644 --- a/server/auth/store_test.go +++ b/server/auth/store_test.go @@ -116,12 +116,16 @@ func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testin // The UserAdd function cannot generate old etcd version user data (user's option is nil) // add special users through the underlying interface - addUserWithNoOption(as) + asImpl, ok := as.(*authStore) + if !ok { + t.Fatal(errors.New("addUserWithNoOption: needs an AuthStore implementation")) + } + addUserWithNoOption(asImpl) tearDown := func(_ *testing.T) { as.Close() } - return as, tearDown + return asImpl, tearDown } func addUserWithNoOption(as *authStore) { @@ -136,7 +140,7 @@ func addUserWithNoOption(as *authStore) { as.refreshRangePermCache(tx) } -func enableAuthAndCreateRoot(as *authStore) error { +func enableAuthAndCreateRoot(as AuthStore) error { _, err := as.UserAdd(&pb.AuthUserAddRequest{Name: "root", HashedPassword: encodePassword("root"), Options: &authpb.UserAddOptions{NoPassword: false}}) if err != nil { return err diff --git a/server/etcdserver/adapters.go b/server/etcdserver/adapters.go index 35660a27bd0..bc9790bc2e6 100644 --- a/server/etcdserver/adapters.go +++ b/server/etcdserver/adapters.go @@ -33,7 +33,7 @@ type serverVersionAdapter struct { *EtcdServer } -func NewServerVersionAdapter(s *EtcdServer) *serverVersionAdapter { +func NewServerVersionAdapter(s *EtcdServer) serverversion.Server { return &serverVersionAdapter{ EtcdServer: s, } diff --git a/server/etcdserver/apply/apply.go b/server/etcdserver/apply/apply.go index e45d53e17b1..b1d24456a8c 100644 --- a/server/etcdserver/apply/apply.go +++ b/server/etcdserver/apply/apply.go @@ -64,6 +64,18 @@ type Result struct { type applyFunc func(r *pb.InternalRaftRequest) *Result +// ApplierMembership defines the applier membership interface. +type ApplierMembership interface { + // ClusterVersionSet sets the version of the cluster. + ClusterVersionSet(r *membershippb.ClusterVersionSetRequest, shouldApplyV3 membership.ShouldApplyV3) + + // ClusterMemberAttrSet sets a cluster member's attributes, if the member is not removed. + ClusterMemberAttrSet(r *membershippb.ClusterMemberAttrSetRequest, shouldApplyV3 membership.ShouldApplyV3) + + // DowngradeInfoSet sets the downgrade info. + DowngradeInfoSet(r *membershippb.DowngradeInfoSetRequest, shouldApplyV3 membership.ShouldApplyV3) +} + // applierV3 is the interface for processing V3 raft messages type applierV3 interface { // Apply executes the generic portion of application logic for the current applier, but @@ -403,7 +415,7 @@ type applierMembership struct { snapshotServer SnapshotServer } -func NewApplierMembership(lg *zap.Logger, cluster *membership.RaftCluster, snapshotServer SnapshotServer) *applierMembership { +func NewApplierMembership(lg *zap.Logger, cluster *membership.RaftCluster, snapshotServer SnapshotServer) ApplierMembership { return &applierMembership{ lg: lg, cluster: cluster, diff --git a/server/mock/mockstorage/storage_recorder.go b/server/mock/mockstorage/storage_recorder.go index 41d2952e8a1..071cffc68ec 100644 --- a/server/mock/mockstorage/storage_recorder.go +++ b/server/mock/mockstorage/storage_recorder.go @@ -18,20 +18,26 @@ import ( "github.com/coreos/go-semver/semver" "go.etcd.io/etcd/client/pkg/v3/testutil" + "go.etcd.io/etcd/server/v3/storage" "go.etcd.io/raft/v3" "go.etcd.io/raft/v3/raftpb" ) +type StorageRecorder interface { + storage.Storage + testutil.Recorder +} + type storageRecorder struct { testutil.Recorder dbPath string // must have '/' suffix if set } -func NewStorageRecorder(db string) *storageRecorder { +func NewStorageRecorder(db string) StorageRecorder { return &storageRecorder{&testutil.RecorderBuffered{}, db} } -func NewStorageRecorderStream(db string) *storageRecorder { +func NewStorageRecorderStream(db string) StorageRecorder { return &storageRecorder{testutil.NewRecorderStream(), db} } diff --git a/server/storage/mvcc/kvstore.go b/server/storage/mvcc/kvstore.go index 3e1226c9174..a96ffd4aad8 100644 --- a/server/storage/mvcc/kvstore.go +++ b/server/storage/mvcc/kvstore.go @@ -82,6 +82,7 @@ type store struct { // NewStore returns a new store. It is useful to create a store inside // mvcc pkg. It should only be used for testing externally. +// revive:disable:unexported-return this is used internally in the mvcc pkg func NewStore(lg *zap.Logger, b backend.Backend, le lease.Lessor, cfg StoreConfig) *store { if lg == nil { lg = zap.NewNop() @@ -132,6 +133,8 @@ func NewStore(lg *zap.Logger, b backend.Backend, le lease.Lessor, cfg StoreConfi return s } +// revive:enable:unexported-return + func (s *store) compactBarrier(ctx context.Context, ch chan struct{}) { if ctx == nil || ctx.Err() != nil { select { diff --git a/server/storage/mvcc/watchable_store.go b/server/storage/mvcc/watchable_store.go index ee47c2c6d72..06375b9a2fb 100644 --- a/server/storage/mvcc/watchable_store.go +++ b/server/storage/mvcc/watchable_store.go @@ -76,7 +76,7 @@ var _ WatchableKV = (*watchableStore)(nil) // cancel operations. type cancelFunc func() -func New(lg *zap.Logger, b backend.Backend, le lease.Lessor, cfg StoreConfig) *watchableStore { +func New(lg *zap.Logger, b backend.Backend, le lease.Lessor, cfg StoreConfig) WatchableKV { s := newWatchableStore(lg, b, le, cfg) s.wg.Add(2) go s.syncWatchersLoop() diff --git a/server/storage/mvcc/watchable_store_test.go b/server/storage/mvcc/watchable_store_test.go index a418c6c78fe..f32d8b2a4ab 100644 --- a/server/storage/mvcc/watchable_store_test.go +++ b/server/storage/mvcc/watchable_store_test.go @@ -15,6 +15,7 @@ package mvcc import ( + "errors" "fmt" "reflect" "sync" @@ -44,7 +45,7 @@ func TestWatch(t *testing.T) { defer w.Close() w.Watch(0, testKey, nil, 0) - if !s.synced.contains(string(testKey)) { + if !s.(*watchableStore).synced.contains(string(testKey)) { // the key must have had an entry in synced t.Errorf("existence = false, want true") } @@ -67,7 +68,7 @@ func TestNewWatcherCancel(t *testing.T) { t.Error(err) } - if s.synced.contains(string(testKey)) { + if s.(*watchableStore).synced.contains(string(testKey)) { // the key shoud have been deleted t.Errorf("existence = true, want false") } @@ -340,7 +341,11 @@ func TestWatchNoEventLossOnCompact(t *testing.T) { require.NoError(t, err) } // fill up w.Chan() with 1 buf via 2 compacted watch response - s.syncWatchers([]mvccpb.Event{}) + sImpl, ok := s.(*watchableStore) + if !ok { + t.Fatal(errors.New("TestWatchNoEventLossOnCompact: needs a WatchableKV implementation")) + } + sImpl.syncWatchers([]mvccpb.Event{}) for len(watchers) > 0 { resp := <-w.Chan() @@ -355,7 +360,7 @@ func TestWatchNoEventLossOnCompact(t *testing.T) { require.Equalf(t, nextRev, ev.Kv.ModRevision, "got event revision %d but want %d for watcher with watch ID %d", ev.Kv.ModRevision, nextRev, resp.WatchID) nextRev++ } - if nextRev == s.rev()+1 { + if nextRev == sImpl.rev()+1 { delete(watchers, resp.WatchID) } } @@ -566,10 +571,15 @@ func TestWatchBatchUnsynced(t *testing.T) { } assert.Equal(t, tc.expectRevisionBatches, revisionBatches) - s.store.revMu.Lock() - defer s.store.revMu.Unlock() - assert.Equal(t, 1, s.synced.size()) - assert.Equal(t, 0, s.unsynced.size()) + sImpl, ok := s.(*watchableStore) + if !ok { + t.Fatal(errors.New("TestWatchBatchUnsynced: needs a WatchableKV implementation")) + } + + sImpl.store.revMu.Lock() + defer sImpl.store.revMu.Unlock() + assert.Equal(t, 1, sImpl.synced.size()) + assert.Equal(t, 0, sImpl.unsynced.size()) }) } } diff --git a/server/storage/schema/alarm.go b/server/storage/schema/alarm.go index 6e81d0f4671..86522cc1eea 100644 --- a/server/storage/schema/alarm.go +++ b/server/storage/schema/alarm.go @@ -21,12 +21,20 @@ import ( "go.etcd.io/etcd/server/v3/storage/backend" ) +type AlarmBackend interface { + CreateAlarmBucket() + MustPutAlarm(alarm *etcdserverpb.AlarmMember) + MustDeleteAlarm(alarm *etcdserverpb.AlarmMember) + GetAllAlarms() ([]*etcdserverpb.AlarmMember, error) + ForceCommit() +} + type alarmBackend struct { lg *zap.Logger be backend.Backend } -func NewAlarmBackend(lg *zap.Logger, be backend.Backend) *alarmBackend { +func NewAlarmBackend(lg *zap.Logger, be backend.Backend) AlarmBackend { return &alarmBackend{ lg: lg, be: be, diff --git a/server/storage/schema/auth.go b/server/storage/schema/auth.go index 96ca881c5c8..b91b6eb6ac0 100644 --- a/server/storage/schema/auth.go +++ b/server/storage/schema/auth.go @@ -40,7 +40,7 @@ type authBackend struct { var _ auth.AuthBackend = (*authBackend)(nil) -func NewAuthBackend(lg *zap.Logger, be backend.Backend) *authBackend { +func NewAuthBackend(lg *zap.Logger, be backend.Backend) auth.AuthBackend { return &authBackend{ be: be, lg: lg, diff --git a/server/storage/schema/membership.go b/server/storage/schema/membership.go index c79ab8b76a9..5f17065ff1d 100644 --- a/server/storage/schema/membership.go +++ b/server/storage/schema/membership.go @@ -32,12 +32,26 @@ const ( MemberRaftAttributesSuffix = "raftAttributes" ) +// MembershipBackend defines the membership backend interface. +type MembershipBackend interface { + MustSaveMemberToBackend(m *membership.Member) + TrimClusterFromBackend() error + MustDeleteMemberFromBackend(id types.ID) + MustReadMembersFromBackend() (map[types.ID]*membership.Member, map[types.ID]bool) + TrimMembershipFromBackend() error + MustSaveClusterVersionToBackend(ver *semver.Version) + MustSaveDowngradeToBackend(downgrade *version.DowngradeInfo) + MustCreateBackendBuckets() + ClusterVersionFromBackend() *semver.Version + DowngradeInfoFromBackend() *version.DowngradeInfo +} + type membershipBackend struct { lg *zap.Logger be backend.Backend } -func NewMembershipBackend(lg *zap.Logger, be backend.Backend) *membershipBackend { +func NewMembershipBackend(lg *zap.Logger, be backend.Backend) MembershipBackend { return &membershipBackend{ lg: lg, be: be, diff --git a/server/storage/schema/schema.go b/server/storage/schema/schema.go index b87b73cc831..84214e3c521 100644 --- a/server/storage/schema/schema.go +++ b/server/storage/schema/schema.go @@ -22,6 +22,7 @@ import ( "go.etcd.io/etcd/api/v3/version" "go.etcd.io/etcd/server/v3/storage/backend" + "go.etcd.io/etcd/server/v3/storage/wal" ) // Validate checks provided backend to confirm that schema used is supported. @@ -47,10 +48,7 @@ func localBinaryVersion() semver.Version { return semver.Version{Major: v.Major, Minor: v.Minor} } -type WALVersion interface { - // MinimalEtcdVersion returns minimal etcd version able to interpret WAL log. - MinimalEtcdVersion() *semver.Version -} +type WALVersion = wal.WALVersion // Migrate updates storage schema to provided target version. // Downgrading requires that provided WAL doesn't contain unsupported entries. diff --git a/server/storage/wal/version.go b/server/storage/wal/version.go index 98592650303..fbdf3600029 100644 --- a/server/storage/wal/version.go +++ b/server/storage/wal/version.go @@ -29,9 +29,14 @@ import ( "go.etcd.io/raft/v3/raftpb" ) +type WALVersion interface { + // MinimalEtcdVersion returns minimal etcd version able to interpret WAL log. + MinimalEtcdVersion() *semver.Version +} + // ReadWALVersion reads remaining entries from opened WAL and returns struct // that implements schema.WAL interface. -func ReadWALVersion(w *WAL) (*walVersion, error) { +func ReadWALVersion(w *WAL) (WALVersion, error) { _, _, ents, err := w.ReadAll() if err != nil { return nil, err