diff --git a/auth/store.go b/auth/store.go index 6d218d313..255404760 100644 --- a/auth/store.go +++ b/auth/store.go @@ -916,7 +916,9 @@ func NewAuthStore(be backend.Backend, indexWaiter func(uint64) <-chan struct{}) as.simpleTokenKeeper = NewSimpleTokenTTLKeeper(newDeleterFunc(as)) } - as.commitRevision(tx) + if as.revision == 0 { + as.commitRevision(tx) + } tx.Unlock() be.ForceCommit() diff --git a/auth/store_test.go b/auth/store_test.go index 3bc3a0014..7116347b8 100644 --- a/auth/store_test.go +++ b/auth/store_test.go @@ -38,6 +38,33 @@ func dummyIndexWaiter(index uint64) <-chan struct{} { return ch } +// TestNewAuthStoreRevision ensures newly auth store +// keeps the old revision when there are no changes. +func TestNewAuthStoreRevision(t *testing.T) { + b, tPath := backend.NewDefaultTmpBackend() + defer os.Remove(tPath) + + as := NewAuthStore(b, dummyIndexWaiter) + err := enableAuthAndCreateRoot(as) + if err != nil { + t.Fatal(err) + } + old := as.Revision() + b.Close() + as.Close() + + // no changes to commit + b2 := backend.NewDefaultBackend(tPath) + as = NewAuthStore(b2, dummyIndexWaiter) + new := as.Revision() + b2.Close() + as.Close() + + if old != new { + t.Fatalf("expected revision %d, got %d", old, new) + } +} + func setupAuthStore(t *testing.T) (store *authStore, teardownfunc func(t *testing.T)) { b, tPath := backend.NewDefaultTmpBackend() diff --git a/integration/v3_grpc_test.go b/integration/v3_grpc_test.go index 65560db66..4d9945b84 100644 --- a/integration/v3_grpc_test.go +++ b/integration/v3_grpc_test.go @@ -855,6 +855,37 @@ func TestV3Hash(t *testing.T) { } } +// TestV3HashRestart ensures that hash stays the same after restart. +func TestV3HashRestart(t *testing.T) { + defer testutil.AfterTest(t) + clus := NewClusterV3(t, &ClusterConfig{Size: 1}) + defer clus.Terminate(t) + + cli := clus.RandClient() + resp, err := toGRPC(cli).Maintenance.Hash(context.Background(), &pb.HashRequest{}) + if err != nil || resp.Hash == 0 { + t.Fatalf("couldn't hash (%v, hash %d)", err, resp.Hash) + } + hash1 := resp.Hash + + clus.Members[0].Stop(t) + clus.Members[0].Restart(t) + clus.waitLeader(t, clus.Members) + kvc := toGRPC(clus.Client(0)).KV + waitForRestart(t, kvc) + + cli = clus.RandClient() + resp, err = toGRPC(cli).Maintenance.Hash(context.Background(), &pb.HashRequest{}) + if err != nil || resp.Hash == 0 { + t.Fatalf("couldn't hash (%v, hash %d)", err, resp.Hash) + } + hash2 := resp.Hash + + if hash1 != hash2 { + t.Fatalf("hash expected %d, got %d", hash1, hash2) + } +} + // TestV3StorageQuotaAPI tests the V3 server respects quotas at the API layer func TestV3StorageQuotaAPI(t *testing.T) { defer testutil.AfterTest(t)