diff --git a/lib/auth/helpers_mfa.go b/lib/auth/helpers_mfa.go
index eca034791db4c..3eddd890b3d4e 100644
--- a/lib/auth/helpers_mfa.go
+++ b/lib/auth/helpers_mfa.go
@@ -33,6 +33,7 @@ import (
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth/mocku2f"
wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
// TestDevice is a test MFA device.
@@ -190,9 +191,8 @@ func (d *TestDevice) solveAuthnTOTP(c *proto.MFAAuthenticateChallenge) (*proto.M
if d.clock == nil {
return nil, trace.BadParameter("clock not set")
}
- if c, ok := d.clock.(clockwork.FakeClock); ok {
- c.Advance(30 * time.Second)
- }
+ clocki.Advance(d.clock, 30*time.Second)
+
code, err := totp.GenerateCode(d.TOTPSecret, d.clock.Now())
if err != nil {
return nil, trace.Wrap(err)
@@ -244,9 +244,7 @@ func (d *TestDevice) solveRegisterTOTP(c *proto.MFARegisterChallenge) (*proto.MF
if d.clock == nil {
return nil, trace.BadParameter("clock not set")
}
- if c, ok := d.clock.(clockwork.FakeClock); ok {
- c.Advance(30 * time.Second)
- }
+ clocki.Advance(d.clock, 30*time.Second)
if c.GetTOTP().Algorithm != otp.AlgorithmSHA1.String() {
return nil, trace.BadParameter("unexpected TOTP challenge algorithm: %s", c.GetTOTP().Algorithm)
diff --git a/lib/backend/dynamo/atomicwrite_test.go b/lib/backend/dynamo/atomicwrite_test.go
index d17255105b558..99ce7b8f652ea 100644
--- a/lib/backend/dynamo/atomicwrite_test.go
+++ b/lib/backend/dynamo/atomicwrite_test.go
@@ -28,9 +28,10 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
-func newAtomicWriteBackend(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+func newAtomicWriteBackend(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
dynamoCfg := map[string]interface{}{
"table_name": dynamoDBTestTable(),
"poll_stream_period": 300 * time.Millisecond,
diff --git a/lib/backend/dynamo/dynamodbbk_test.go b/lib/backend/dynamo/dynamodbbk_test.go
index 4c6739d702c54..803310c86fd55 100644
--- a/lib/backend/dynamo/dynamodbbk_test.go
+++ b/lib/backend/dynamo/dynamodbbk_test.go
@@ -43,6 +43,7 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
"github.com/gravitational/teleport/lib/utils"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
func TestMain(m *testing.M) {
@@ -74,7 +75,7 @@ func TestDynamoDB(t *testing.T) {
"poll_stream_period": 300 * time.Millisecond,
}
- newBackend := func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+ newBackend := func(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
testCfg, err := test.ApplyOptions(options)
if err != nil {
return nil, nil, trace.Wrap(err)
diff --git a/lib/backend/etcdbk/atomicwrite_test.go b/lib/backend/etcdbk/atomicwrite_test.go
index 25369f05f4703..ac64bd3ae007b 100644
--- a/lib/backend/etcdbk/atomicwrite_test.go
+++ b/lib/backend/etcdbk/atomicwrite_test.go
@@ -23,15 +23,15 @@ import (
"testing"
"github.com/gravitational/trace"
- "github.com/jonboulle/clockwork"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
// newAtomicWriteTestBackend builds a backend suitable for the atomic write test suite. Once all backends implement AtomicWrite,
// it will be integrated into the main backend interface and we can get rid of this separate helper.
-func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
opts, err := test.ApplyOptions(options)
if err != nil {
return nil, nil, trace.Wrap(err)
diff --git a/lib/backend/etcdbk/etcd_test.go b/lib/backend/etcdbk/etcd_test.go
index 7adc7cebb91f4..ac6780ed1ef9d 100644
--- a/lib/backend/etcdbk/etcd_test.go
+++ b/lib/backend/etcdbk/etcd_test.go
@@ -28,13 +28,13 @@ import (
"time"
"github.com/gravitational/trace"
- "github.com/jonboulle/clockwork"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
"github.com/gravitational/teleport/lib/utils"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
const (
@@ -65,7 +65,7 @@ func TestEtcd(t *testing.T) {
t.Skip("This test requires etcd, run `make run-etcd` and set TELEPORT_ETCD_TEST=yes in your environment")
}
- newBackend := func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+ newBackend := func(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
opts, err := test.ApplyOptions(options)
if err != nil {
return nil, nil, trace.Wrap(err)
diff --git a/lib/backend/firestore/atomicwrite_test.go b/lib/backend/firestore/atomicwrite_test.go
index 1f20b1fa244cf..b1290f85af623 100644
--- a/lib/backend/firestore/atomicwrite_test.go
+++ b/lib/backend/firestore/atomicwrite_test.go
@@ -27,9 +27,10 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
-func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
cfg := firestoreParams()
testCfg, err := test.ApplyOptions(options)
diff --git a/lib/backend/firestore/firestorebk_test.go b/lib/backend/firestore/firestorebk_test.go
index baf93c6184fac..26bd799f8a5de 100644
--- a/lib/backend/firestore/firestorebk_test.go
+++ b/lib/backend/firestore/firestorebk_test.go
@@ -53,6 +53,7 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
"github.com/gravitational/teleport/lib/utils"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
func TestMain(m *testing.M) {
@@ -126,7 +127,7 @@ func TestFirestoreDB(t *testing.T) {
ensureTestsEnabled(t)
ensureEmulatorRunning(t, cfg)
- newBackend := func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+ newBackend := func(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
testCfg, err := test.ApplyOptions(options)
if err != nil {
return nil, nil, trace.Wrap(err)
diff --git a/lib/backend/lite/atomicwrite_test.go b/lib/backend/lite/atomicwrite_test.go
index 543a0431586e7..f2d8709d79feb 100644
--- a/lib/backend/lite/atomicwrite_test.go
+++ b/lib/backend/lite/atomicwrite_test.go
@@ -28,12 +28,13 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
// newAtomicWriteTestBackendBuilder builds a backend suitable for the atomic write test suite. Once all backends implement AtomicWrite,
// it will be integrated into the main backend interface and we can get rid of this separate helper.
-func newAtomicWriteTestBackendBuilder(t *testing.T) func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
- return func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+func newAtomicWriteTestBackendBuilder(t *testing.T) test.Constructor[clocki.FakeClock] {
+ return func(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
clock := clockwork.NewFakeClock()
cfg, err := test.ApplyOptions(options)
diff --git a/lib/backend/lite/lite_test.go b/lib/backend/lite/lite_test.go
index b401b595ebbcd..e3383b3082802 100644
--- a/lib/backend/lite/lite_test.go
+++ b/lib/backend/lite/lite_test.go
@@ -31,6 +31,7 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
"github.com/gravitational/teleport/lib/utils"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
func TestMain(m *testing.M) {
@@ -39,7 +40,7 @@ func TestMain(m *testing.M) {
}
func TestLite(t *testing.T) {
- newBackend := func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+ newBackend := func(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
clock := clockwork.NewFakeClock()
cfg, err := test.ApplyOptions(options)
diff --git a/lib/backend/memory/atomicwrite_test.go b/lib/backend/memory/atomicwrite_test.go
index 95314c7776e64..a3bf7747855d4 100644
--- a/lib/backend/memory/atomicwrite_test.go
+++ b/lib/backend/memory/atomicwrite_test.go
@@ -26,11 +26,12 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
// newAtomicWriteTestBackend builds a backend suitable for the atomic write test suite. Once all backends implement AtomicWrite,
// it will be integrated into the main backend interface and we can get rid of this separate helper.
-func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
cfg, err := test.ApplyOptions(options)
if err != nil {
diff --git a/lib/backend/memory/memory_test.go b/lib/backend/memory/memory_test.go
index 46e8b7532fdfa..442b97343a0ee 100644
--- a/lib/backend/memory/memory_test.go
+++ b/lib/backend/memory/memory_test.go
@@ -32,6 +32,7 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
"github.com/gravitational/teleport/lib/utils"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
func TestMain(m *testing.M) {
@@ -40,7 +41,7 @@ func TestMain(m *testing.M) {
}
func TestMemory(t *testing.T) {
- newBackend := func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+ newBackend := func(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
cfg, err := test.ApplyOptions(options)
if err != nil {
diff --git a/lib/backend/pgbk/atomicwrite_test.go b/lib/backend/pgbk/atomicwrite_test.go
index d694b1de968fe..5f0379eb33a41 100644
--- a/lib/backend/pgbk/atomicwrite_test.go
+++ b/lib/backend/pgbk/atomicwrite_test.go
@@ -29,6 +29,7 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
// Testing requires a local psql backend to be set up, and for params to be passed via env. Ex:
@@ -37,7 +38,7 @@ import (
// newAtomicWriteTestBackend builds a backend suitable for the atomic write test suite. Once all backends implement AtomicWrite,
// it will be integrated into the main backend interface and we can get rid of this separate helper.
-func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+func newAtomicWriteTestBackend(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
testCfg, err := test.ApplyOptions(options)
if err != nil {
return nil, nil, trace.Wrap(err)
diff --git a/lib/backend/pgbk/pgbk_test.go b/lib/backend/pgbk/pgbk_test.go
index 11471ebe638ee..fe4e66d439144 100644
--- a/lib/backend/pgbk/pgbk_test.go
+++ b/lib/backend/pgbk/pgbk_test.go
@@ -31,6 +31,7 @@ import (
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/backend/test"
"github.com/gravitational/teleport/lib/utils"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
func TestMain(m *testing.M) {
@@ -47,7 +48,7 @@ func TestPostgresBackend(t *testing.T) {
t.Skip("Postgres backend tests are disabled. Enable them by setting the TELEPORT_PGBK_TEST_PARAMS_JSON variable.")
}
- newBackend := func(options ...test.ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+ newBackend := func(options ...test.ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
testCfg, err := test.ApplyOptions(options)
if err != nil {
return nil, nil, trace.Wrap(err)
diff --git a/lib/backend/test/atomicwrite.go b/lib/backend/test/atomicwrite.go
index eb2a6a566af96..756d232623e42 100644
--- a/lib/backend/test/atomicwrite.go
+++ b/lib/backend/test/atomicwrite.go
@@ -30,9 +30,10 @@ import (
"golang.org/x/sync/errgroup"
"github.com/gravitational/teleport/lib/backend"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
-func RunAtomicWriteComplianceSuite(t *testing.T, newBackend Constructor) {
+func RunAtomicWriteComplianceSuite[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
t.Run("Move", func(t *testing.T) {
testAtomicWriteMove(t, newBackend)
})
@@ -59,7 +60,7 @@ func RunAtomicWriteComplianceSuite(t *testing.T, newBackend Constructor) {
}
// testAtomicWriteMove verifies the correct behavior of "move" operations.
-func testAtomicWriteMove(t *testing.T, newBackend Constructor) {
+func testAtomicWriteMove[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
bk, _, err := newBackend()
require.NoError(t, err)
@@ -121,7 +122,7 @@ func testAtomicWriteMove(t *testing.T, newBackend Constructor) {
// testAtomicWriteLock verifies correct behavior of various "lock" patterns (i.e. where some update on key X is conditional on
// the state of key Y).
-func testAtomicWriteLock(t *testing.T, newBackend Constructor) {
+func testAtomicWriteLock[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
bk, _, err := newBackend()
require.NoError(t, err)
@@ -275,7 +276,7 @@ func testAtomicWriteLock(t *testing.T, newBackend Constructor) {
}
// testAtomicWriteMax verifies correct behavior of very large atomic writes.
-func testAtomicWriteMax(t *testing.T, newBackend Constructor) {
+func testAtomicWriteMax[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
bk, _, err := newBackend()
require.NoError(t, err)
@@ -361,7 +362,7 @@ func testAtomicWriteMax(t *testing.T, newBackend Constructor) {
}
// testAtomicWriteConcurrent is a sanity-check intended to verify the correctness of AtomicWrite under high concurrency.
-func testAtomicWriteConcurrent(t *testing.T, newBackend Constructor) {
+func testAtomicWriteConcurrent[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
const (
increments = 200
workers = 20
@@ -454,7 +455,7 @@ func testAtomicWriteConcurrent(t *testing.T, newBackend Constructor) {
// testAtomicWriteNonConflicting verifies that non-conflicting but overlapping transactions all succeed
// on the first attempt when running concurrently, meaning that backends that treat overlap as conflict (e.g. dynamo)
// handle such conflicts internally.
-func testAtomicWriteNonConflicting(t *testing.T, newBackend Constructor) {
+func testAtomicWriteNonConflicting[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
const workers = 60
bk, _, err := newBackend()
@@ -521,7 +522,7 @@ func testAtomicWriteNonConflicting(t *testing.T, newBackend Constructor) {
// testAtomicWriteOther verifies some minor edge-cases that may not be covered by other tests. Specifically,
// it verifies that Item.Key has no effect on writes or subsequent reads, and that ineffectual writes still
// update the value of revision.
-func testAtomicWriteOther(t *testing.T, newBackend Constructor) {
+func testAtomicWriteOther[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
bk, _, err := newBackend()
require.NoError(t, err)
diff --git a/lib/backend/test/atomicwrite_shim.go b/lib/backend/test/atomicwrite_shim.go
index d2df92aa07224..937de22eddeeb 100644
--- a/lib/backend/test/atomicwrite_shim.go
+++ b/lib/backend/test/atomicwrite_shim.go
@@ -26,17 +26,17 @@ import (
"github.com/google/uuid"
"github.com/gravitational/trace"
- "github.com/jonboulle/clockwork"
"github.com/gravitational/teleport/lib/backend"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
// RunBackendComplianceSuiteWithAtomicWriteShim runs the old backend compliance suite against the provided backend
// with a shim that converts all calls to single-write methods (all write methods but DeleteRange) into calls to
// AtomicWrite. This is done to ensure that the relationship between the conditional actions of AtomicWrite and the
// single-write methods is well defined, and to improve overall coverage of AtomicWrite implementations via reuse.
-func RunBackendComplianceSuiteWithAtomicWriteShim(t *testing.T, newBackend Constructor) {
- RunBackendComplianceSuite(t, func(options ...ConstructionOption) (backend.Backend, clockwork.FakeClock, error) {
+func RunBackendComplianceSuiteWithAtomicWriteShim[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
+ RunBackendComplianceSuite(t, func(options ...ConstructionOption) (backend.Backend, clocki.FakeClock, error) {
bk, clock, err := newBackend(options...)
if err != nil {
return nil, nil, trace.Wrap(err)
diff --git a/lib/backend/test/suite.go b/lib/backend/test/suite.go
index ec00969240a6b..e0b3a55d8f524 100644
--- a/lib/backend/test/suite.go
+++ b/lib/backend/test/suite.go
@@ -40,6 +40,7 @@ import (
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/backend"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
var (
@@ -123,7 +124,7 @@ func (r BlockingFakeClock) BlockUntil(int) {
// Constructor describes a function for constructing new instances of a
// backend, with various options as required by a given test. Note that
// it's the caller's responsibility to close it when the test is finished.
-type Constructor func(options ...ConstructionOption) (backend.Backend, clockwork.FakeClock, error)
+type Constructor[FakeClock clocki.FakeClock] func(options ...ConstructionOption) (backend.Backend, FakeClock, error)
// RunBackendComplianceSuite runs the entire backend compliance suite,
// creating a collection of named subtests under the context provided
@@ -132,7 +133,7 @@ type Constructor func(options ...ConstructionOption) (backend.Backend, clockwork
// As each test requires a new backend instance it will invoke the supplied
// `newBackend` function, which callers will use inject instances of the
// backend under test.
-func RunBackendComplianceSuite(t *testing.T, newBackend Constructor) {
+func RunBackendComplianceSuite[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
t.Run("CRUD", func(t *testing.T) {
testCRUD(t, newBackend)
})
@@ -206,7 +207,7 @@ func RequireItems(t *testing.T, expected, actual []backend.Item) {
}
// testCRUD tests create read update scenarios
-func testCRUD(t *testing.T, newBackend Constructor) {
+func testCRUD[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, _, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -295,7 +296,7 @@ func testCRUD(t *testing.T, newBackend Constructor) {
require.Equal(t, lease.Revision, out.Revision)
}
-func testQueryRange(t *testing.T, newBackend Constructor) {
+func testQueryRange[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, _, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -357,7 +358,7 @@ func testQueryRange(t *testing.T, newBackend Constructor) {
}
// testDeleteRange tests delete items by range
-func testDeleteRange(t *testing.T, newBackend Constructor) {
+func testDeleteRange[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, _, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -396,7 +397,7 @@ func testDeleteRange(t *testing.T, newBackend Constructor) {
}
// testCompareAndSwap tests compare and swap functionality
-func testCompareAndSwap(t *testing.T, newBackend Constructor) {
+func testCompareAndSwap[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, clock, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -473,7 +474,7 @@ func testCompareAndSwap(t *testing.T, newBackend Constructor) {
}
// testExpiration tests scenario with expiring values
-func testExpiration(t *testing.T, newBackend Constructor) {
+func testExpiration[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, clock, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -505,7 +506,7 @@ func addSeconds(t time.Time, seconds int64) time.Time {
}
// testKeepAlive tests keep alive API
-func testKeepAlive(t *testing.T, newBackend Constructor) {
+func testKeepAlive[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
const eventTimeout = 10 * time.Second
uut, clock, err := newBackend()
@@ -567,7 +568,7 @@ func testKeepAlive(t *testing.T, newBackend Constructor) {
}
// testEvents tests scenarios with event watches
-func testEvents(t *testing.T, newBackend Constructor) {
+func testEvents[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
const eventTimeout = 10 * time.Second
var ttlDeleteTimeout = eventTimeout
// TELEPORT_BACKEND_TEST_TTL_DELETE_TIMEOUT may be set to extend the time waited
@@ -649,7 +650,7 @@ func testEvents(t *testing.T, newBackend Constructor) {
}
// testFetchLimit tests fetch max items size limit.
-func testFetchLimit(t *testing.T, newBackend Constructor) {
+func testFetchLimit[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, _, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -674,7 +675,7 @@ func testFetchLimit(t *testing.T, newBackend Constructor) {
}
// testLimit tests limit.
-func testLimit(t *testing.T, newBackend Constructor) {
+func testLimit[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, clock, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -751,7 +752,7 @@ func requireNoEvent(t *testing.T, watcher backend.Watcher, timeout time.Duration
}
// WatchersClose tests scenarios with watches close
-func testWatchersClose(t *testing.T, newBackend Constructor) {
+func testWatchersClose[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, _, err := newBackend()
require.NoError(t, err)
@@ -792,7 +793,7 @@ func testWatchersClose(t *testing.T, newBackend Constructor) {
}
// testLocking tests locking logic
-func testLocking(t *testing.T, newBackend Constructor) {
+func testLocking[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
tok1 := "token1"
tok2 := "token2"
ttl := 30 * time.Second
@@ -927,7 +928,7 @@ func testLocking(t *testing.T, newBackend Constructor) {
// testConcurrentOperations tests concurrent operations on the same
// shared backend
-func testConcurrentOperations(t *testing.T, newBackend Constructor) {
+func testConcurrentOperations[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uutA, _, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uutA.Close()) }()
@@ -1022,7 +1023,7 @@ func testConcurrentOperations(t *testing.T, newBackend Constructor) {
// Mirror tests mirror mode for backends (used in caches). Only some backends
// support mirror mode (like memory).
-func testMirror(t *testing.T, newBackend Constructor) {
+func testMirror[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
const eventTimeout = 2 * time.Second
uut, _, err := newBackend(WithMirrorMode(true))
@@ -1103,7 +1104,7 @@ func testMirror(t *testing.T, newBackend Constructor) {
require.NoError(t, err)
}
-func testConditionalDelete(t *testing.T, newBackend Constructor) {
+func testConditionalDelete[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, _, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
@@ -1143,7 +1144,7 @@ func testConditionalDelete(t *testing.T, newBackend Constructor) {
require.ErrorIs(t, err, backend.ErrIncorrectRevision)
}
-func testConditionalUpdate(t *testing.T, newBackend Constructor) {
+func testConditionalUpdate[FakeClock clocki.FakeClock](t *testing.T, newBackend Constructor[FakeClock]) {
uut, _, err := newBackend()
require.NoError(t, err)
defer func() { require.NoError(t, uut.Close()) }()
diff --git a/lib/srv/mock.go b/lib/srv/mock.go
index 548019d2928d6..cda6f938a9dc5 100644
--- a/lib/srv/mock.go
+++ b/lib/srv/mock.go
@@ -47,6 +47,7 @@ import (
rsession "github.com/gravitational/teleport/lib/session"
"github.com/gravitational/teleport/lib/sshutils"
"github.com/gravitational/teleport/lib/utils"
+ "github.com/gravitational/teleport/lib/utils/clocki"
)
func newTestServerContext(t *testing.T, srv Server, roleSet services.RoleSet) *ServerContext {
@@ -159,7 +160,7 @@ type mockServer struct {
datadir string
auth *auth.Server
component string
- clock clockwork.FakeClock
+ clock clocki.FakeClock
bpf bpf.BPF
}
diff --git a/lib/utils/clocki/advance.go b/lib/utils/clocki/advance.go
new file mode 100644
index 0000000000000..12cb83baba172
--- /dev/null
+++ b/lib/utils/clocki/advance.go
@@ -0,0 +1,31 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package clocki
+
+import (
+ "time"
+
+ "github.com/jonboulle/clockwork"
+)
+
+// Advance attempts to advance an underlying fake clock.
+// It's a noop on real clocks.
+func Advance(clock clockwork.Clock, d time.Duration) {
+ if c, ok := clock.(interface{ Advance(time.Duration) }); ok {
+ c.Advance(d)
+ }
+}
diff --git a/lib/utils/clocki/doc.go b/lib/utils/clocki/doc.go
new file mode 100644
index 0000000000000..1d100b81bc4a0
--- /dev/null
+++ b/lib/utils/clocki/doc.go
@@ -0,0 +1,19 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+// Package clocki holds interfaces and utilities to deal with clockwork that
+// are resilient to its breaking changes.
+package clocki
diff --git a/lib/utils/clocki/fake_clock.go b/lib/utils/clocki/fake_clock.go
new file mode 100644
index 0000000000000..f0be42cb32dfa
--- /dev/null
+++ b/lib/utils/clocki/fake_clock.go
@@ -0,0 +1,35 @@
+// Teleport
+// Copyright (C) 2025 Gravitational, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package clocki
+
+import (
+ "time"
+
+ "github.com/jonboulle/clockwork"
+)
+
+// FakeClock duplicates its namesake interface as defined by clockwork versions
+// prior to v0.5.0.
+type FakeClock interface {
+ clockwork.Clock
+
+ // Advance advances the FakeClock to a new point in time, ensuring any existing
+ // waiters are notified appropriately before returning.
+ Advance(d time.Duration)
+ // BlockUntil blocks until the FakeClock has the given number of waiters.
+ BlockUntil(waiters int)
+}