Paginate targets in cache (#5101)

* Update Go API to support pagination
* Implement pagination of targets in cache

(cherry picked from commit cd25fad42f325fc050b7ea3bec3336780d2b5e16)
This commit is contained in:
Jeff Mitchell authored and Timothy Messier committed 2024-09-24 14:18:53 +00:00
1 parent 2a87dcad48
commit 3a4ee032d9
62 files changed
+3875 -1533

No files matched your search

+9
View File
@@ -50,6 +50,7 @@ type StartCommand struct {
flagLogFormat string
flagStoreDebug bool
flagBackground bool
flagForceResetSchema bool
}
func (c *StartCommand) Synopsis() string {
@@ -133,6 +134,13 @@ func (c *StartCommand) Flags() *base.FlagSets {
Default: false,
Usage: `Run the cache daemon in the background`,
})
f.BoolVar(&base.BoolVar{
Name: "force-reset-schema",
Target: &c.flagForceResetSchema,
Default: false,
Usage: `Force resetting the cache schema and all contained data`,
Hidden: true,
})
return set
}
@@ -207,6 +215,7 @@ func (c *StartCommand) Run(args []string) int {
LogFileName: logFileName,
DotDirectory: dotDir,
RunningInBackground: os.Getenv(backgroundEnvName) == backgroundEnvVal,
ForceResetSchema: c.flagForceResetSchema,
}
srv, err := daemon.New(ctx, cfg)
+10
View File
@@ -25,6 +25,7 @@ type options struct {
withIgnoreSearchStaleness bool
withMaxResultSetSize int
withTestRefreshWaitChs *testRefreshWaitChs
withUseNonPagedListing bool
}
// Option - how options are passed as args
@@ -129,3 +130,12 @@ func WithTestRefreshWaitChs(with *testRefreshWaitChs) Option {
return nil
}
}
// WithUseNonPagedListing provides an option for ignoring the resource
// staleness when performing a search.
func WithUseNonPagedListing(b bool) Option {
return func(o *options) error {
o.withUseNonPagedListing = b
return nil
}
}
+10 -2
View File
@@ -49,8 +49,8 @@ func Test_GetOpts(t *testing.T) {
assert.Equal(t, opts, testOpts)
})
t.Run("WithTargetRetrievalFunc", func(t *testing.T) {
var f TargetRetrievalFunc = func(ctx context.Context, addr, authTok string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
return nil, nil, "", nil
var f TargetRetrievalFunc = func(ctx context.Context, addr, authTok string, refreshTok RefreshTokenValue, inPage *targets.TargetListResult, opt ...Option) (*targets.TargetListResult, RefreshTokenValue, error) {
return nil, "", nil
}
opts, err := getOpts(WithTargetRetrievalFunc(f))
require.NoError(t, err)
@@ -118,4 +118,12 @@ func Test_GetOpts(t *testing.T) {
testOpts.withTestRefreshWaitChs = waitCh
assert.Equal(t, opts, testOpts)
})
t.Run("WithUseNonPagedListing", func(t *testing.T) {
opts, err := getOpts(WithUseNonPagedListing(true))
require.NoError(t, err)
testOpts := getDefaultOptions()
assert.False(t, testOpts.withUseNonPagedListing)
testOpts.withUseNonPagedListing = true
assert.Equal(t, opts, testOpts)
})
}
+38 -19
View File
@@ -420,28 +420,47 @@ func (r *RefreshService) RecheckCachingSupport(ctx context.Context, opt ...Optio
continue
}
if err := r.repo.checkCachingTargets(ctx, u, tokens, opt...); err != nil {
if err == ErrRefreshNotSupported {
// This is expected so no need to propagate the error up
continue
cacheKey := fmt.Sprintf("%s-%s", u.Id, Targets)
semaphore, _ := r.syncSemaphores.LoadOrStore(cacheKey, new(atomic.Bool))
if semaphore.(*atomic.Bool).CompareAndSwap(false, true) {
if err := r.repo.checkCachingTargets(ctx, u, tokens, opt...); err != nil {
if err == ErrRefreshNotSupported {
semaphore.(*atomic.Bool).Store(false)
// This is expected so no need to propagate the error up
continue
}
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for user id %s", u.Id))))
}
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for user id %s", u.Id))))
}
if err := r.repo.checkCachingSessions(ctx, u, tokens, opt...); err != nil {
if err == ErrRefreshNotSupported {
// This is expected so no need to propagate the error up
continue
}
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for user id %s", u.Id))))
}
if err := r.repo.checkCachingResolvableAliases(ctx, u, tokens, opt...); err != nil {
if err == ErrRefreshNotSupported {
// This is expected so no need to propagate the error up
continue
}
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for user id %s", u.Id))))
semaphore.(*atomic.Bool).Store(false)
}
cacheKey = fmt.Sprintf("%s-%s", u.Id, ResolvableAliases)
semaphore, _ = r.syncSemaphores.LoadOrStore(cacheKey, new(atomic.Bool))
if semaphore.(*atomic.Bool).CompareAndSwap(false, true) {
if err := r.repo.checkCachingResolvableAliases(ctx, u, tokens, opt...); err != nil {
if err == ErrRefreshNotSupported {
// This is expected so no need to propagate the error up
semaphore.(*atomic.Bool).Store(false)
continue
}
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for user id %s", u.Id))))
}
semaphore.(*atomic.Bool).Store(false)
}
cacheKey = fmt.Sprintf("%s-%s", u.Id, Sessions)
semaphore, _ = r.syncSemaphores.LoadOrStore(cacheKey, new(atomic.Bool))
if semaphore.(*atomic.Bool).CompareAndSwap(false, true) {
if err := r.repo.checkCachingSessions(ctx, u, tokens, opt...); err != nil {
if err == ErrRefreshNotSupported {
semaphore.(*atomic.Bool).Store(false)
// This is expected so no need to propagate the error up
continue
}
retErr = stderrors.Join(retErr, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for user id %s", u.Id))))
}
semaphore.(*atomic.Bool).Store(false)
}
}
return retErr
}
+60 -43
View File
@@ -54,6 +54,22 @@ func testStaticResourceRetrievalFunc[T any](t *testing.T, ret [][]T, removed [][
}
}
func testTargetStaticResourceRetrievalFunc(inFunc func(ctx context.Context, s1, s2 string, refToken RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error)) TargetRetrievalFunc {
return func(ctx context.Context, addr, authTok string, refreshTok RefreshTokenValue, inPage *targets.TargetListResult, opt ...Option) (ret *targets.TargetListResult, refreshToken RefreshTokenValue, err error) {
retTargets, removed, refreshToken, err := inFunc(ctx, addr, authTok, refreshTok)
if err != nil {
return nil, "", err
}
ret = &targets.TargetListResult{
Items: retTargets,
RemovedIds: removed,
ResponseType: "complete",
}
return ret, refreshToken, nil
}
}
// testNoRefreshRetrievalFunc simulates a controller that doesn't support refresh
// since it does not return any refresh token.
func testNoRefreshRetrievalFunc[T any](t *testing.T) func(context.Context, string, string, RefreshTokenValue) ([]T, []string, RefreshTokenValue, error) {
@@ -426,7 +442,7 @@ func TestRefreshForSearch(t *testing.T) {
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
[][]*targets.Target{
retTargets[:3],
retTargets[3:],
@@ -435,7 +451,7 @@ func TestRefreshForSearch(t *testing.T) {
nil,
{retTargets[0].Id, retTargets[1].Id},
},
)),
))),
}
assert.NoError(t, rs.RefreshForSearch(ctx, at.Id, Targets, opts...))
@@ -481,7 +497,7 @@ func TestRefreshForSearch(t *testing.T) {
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
[][]*targets.Target{
retTargets[:3],
retTargets[3:],
@@ -490,7 +506,7 @@ func TestRefreshForSearch(t *testing.T) {
nil,
{retTargets[0].Id, retTargets[1].Id},
},
)),
))),
}
assert.NoError(t, rs.RefreshForSearch(ctx, at.Id, Targets, opts...))
@@ -538,8 +554,8 @@ func TestRefreshForSearch(t *testing.T) {
// Get the first set of resources, but no refresh tokens
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)))
assert.ErrorContains(t, err, ErrRefreshNotSupported.Error())
got, err := r.ListTargets(ctx, at.Id)
@@ -554,14 +570,14 @@ func TestRefreshForSearch(t *testing.T) {
// any more.
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)))
assert.Nil(t, err)
err = rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)))
assert.Nil(t, err)
got, err = r.ListTargets(ctx, at.Id)
@@ -575,8 +591,9 @@ func TestRefreshForSearch(t *testing.T) {
// the resources starting to be cached.
err = rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, [][]*targets.Target{retTargets}, [][]string{{}}))),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, [][]*targets.Target{retTargets}, [][]string{{}})))
)
assert.Nil(t, err, err)
got, err = r.ListTargets(ctx, at.Id)
@@ -601,7 +618,7 @@ func TestRefreshForSearch(t *testing.T) {
}
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t,
[][]*sessions.Session{
retSess[:3],
@@ -653,7 +670,7 @@ func TestRefreshForSearch(t *testing.T) {
}
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t,
[][]*sessions.Session{
retSess[:3],
@@ -710,7 +727,7 @@ func TestRefreshForSearch(t *testing.T) {
}
opts := []Option{
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t,
[][]*aliases.Alias{
retAl[:3],
@@ -762,7 +779,7 @@ func TestRefreshForSearch(t *testing.T) {
}
opts := []Option{
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t,
[][]*aliases.Alias{
retAls[:3],
@@ -839,7 +856,7 @@ func TestRefreshNonBlocking(t *testing.T) {
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
[][]*targets.Target{
retTargets[:3],
retTargets[3:],
@@ -848,7 +865,7 @@ func TestRefreshNonBlocking(t *testing.T) {
nil,
{retTargets[0].Id, retTargets[1].Id},
},
)),
))),
}
refreshWaitChs := &testRefreshWaitChs{
@@ -899,7 +916,7 @@ func TestRefreshNonBlocking(t *testing.T) {
}
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t,
[][]*sessions.Session{
retSess[:3],
@@ -961,7 +978,7 @@ func TestRefreshNonBlocking(t *testing.T) {
}
opts := []Option{
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t,
[][]*aliases.Alias{
retAl[:3],
@@ -1041,7 +1058,7 @@ func TestRefresh(t *testing.T) {
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t,
[][]*targets.Target{
retTargets[:3],
retTargets[3:],
@@ -1050,7 +1067,7 @@ func TestRefresh(t *testing.T) {
nil,
{retTargets[0].Id, retTargets[1].Id},
},
)),
))),
}
assert.NoError(t, rs.Refresh(ctx, opts...))
@@ -1082,7 +1099,7 @@ func TestRefresh(t *testing.T) {
}
opts := []Option{
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t,
[][]*sessions.Session{
retSess[:3],
@@ -1123,7 +1140,7 @@ func TestRefresh(t *testing.T) {
}
opts := []Option{
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t,
[][]*aliases.Alias{
retAls[:3],
@@ -1160,15 +1177,15 @@ func TestRefresh(t *testing.T) {
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(func(ctx context.Context, addr, token string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(func(ctx context.Context, addr, token string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
require.Equal(t, boundaryAddr, addr)
require.Equal(t, at.Token, token)
return nil, nil, "", innerErr
}))
})))
assert.ErrorContains(t, err, innerErr.Error())
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))),
WithSessionRetrievalFunc(func(ctx context.Context, addr, token string, refreshTok RefreshTokenValue) ([]*sessions.Session, []string, RefreshTokenValue, error) {
require.Equal(t, boundaryAddr, addr)
require.Equal(t, at.Token, token)
@@ -1202,7 +1219,7 @@ func TestRefresh(t *testing.T) {
require.NoError(t, rs.Refresh(ctx,
WithAliasRetrievalFunc(testStaticResourceRetrievalFuncForId[*aliases.Alias](t, nil, nil)),
WithSessionRetrievalFunc(testStaticResourceRetrievalFunc[*sessions.Session](t, nil, nil)),
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc[*targets.Target](t, nil, nil)))))
ps, err = r.listTokens(ctx, u)
require.NoError(t, err)
@@ -1246,7 +1263,7 @@ func TestRecheckCachingSupport(t *testing.T) {
assert.NoError(t, rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))))
got, err := r.ListTargets(ctx, at.Id)
require.NoError(t, err)
@@ -1258,7 +1275,7 @@ func TestRecheckCachingSupport(t *testing.T) {
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))))
assert.ErrorIs(t, err, ErrRefreshNotSupported)
got, err = r.ListTargets(ctx, at.Id)
@@ -1272,7 +1289,7 @@ func TestRecheckCachingSupport(t *testing.T) {
assert.NoError(t, rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))))
})
t.Run("sessions", func(t *testing.T) {
@@ -1286,7 +1303,7 @@ func TestRecheckCachingSupport(t *testing.T) {
assert.NoError(t, rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t))))
got, err := r.ListSessions(ctx, at.Id)
@@ -1298,7 +1315,7 @@ func TestRecheckCachingSupport(t *testing.T) {
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)))
assert.ErrorIs(t, err, ErrRefreshNotSupported)
@@ -1311,7 +1328,7 @@ func TestRecheckCachingSupport(t *testing.T) {
assert.NoError(t, rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t))))
got, err = r.ListSessions(ctx, at.Id)
require.NoError(t, err)
@@ -1332,7 +1349,7 @@ func TestRecheckCachingSupport(t *testing.T) {
assert.NoError(t, rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t))))
got, err := r.ListResolvableAliases(ctx, at.Id)
@@ -1344,7 +1361,7 @@ func TestRecheckCachingSupport(t *testing.T) {
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)))
assert.ErrorIs(t, err, ErrRefreshNotSupported)
@@ -1357,7 +1374,7 @@ func TestRecheckCachingSupport(t *testing.T) {
assert.NoError(t, rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t))))
got, err = r.ListResolvableAliases(ctx, at.Id)
require.NoError(t, err)
@@ -1378,7 +1395,7 @@ func TestRecheckCachingSupport(t *testing.T) {
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)))
assert.ErrorIs(t, err, ErrRefreshNotSupported)
@@ -1386,21 +1403,21 @@ func TestRecheckCachingSupport(t *testing.T) {
err = rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(func(ctx context.Context, addr, token string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(func(ctx context.Context, addr, token string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
require.Equal(t, boundaryAddr, addr)
require.Equal(t, at.Token, token)
return nil, nil, "", innerErr
}))
})))
assert.ErrorContains(t, err, innerErr.Error())
err = rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(func(ctx context.Context, addr, token string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(func(ctx context.Context, addr, token string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
require.Equal(t, boundaryAddr, addr)
require.Equal(t, at.Token, token)
return nil, nil, "", innerErr
}))
})))
assert.ErrorContains(t, err, innerErr.Error())
})
@@ -1415,7 +1432,7 @@ func TestRecheckCachingSupport(t *testing.T) {
err = rs.Refresh(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)),
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)))
assert.ErrorIs(t, err, ErrRefreshNotSupported)
@@ -1435,7 +1452,7 @@ func TestRecheckCachingSupport(t *testing.T) {
err = rs.RecheckCachingSupport(ctx,
WithAliasRetrievalFunc(testNoRefreshRetrievalFuncForId[*aliases.Alias](t)),
WithSessionRetrievalFunc(testNoRefreshRetrievalFunc[*sessions.Session](t)),
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))))
assert.NoError(t, err)
ps, err = r.listTokens(ctx, u)
@@ -62,7 +62,7 @@ func TestRepository_ImplicitScopes(t *testing.T) {
target("3"),
}
require.NoError(t, r.refreshTargets(ctx, u1, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil})))))
for _, t := range ts {
expectedScopes = append(expectedScopes, &scopes.Scope{
+72 -47
View File
@@ -21,32 +21,42 @@ import (
// TargetRetrievalFunc is a function that retrieves targets
// from the provided boundary addr using the provided token.
type TargetRetrievalFunc func(ctx context.Context, addr, authTok string, refreshTok RefreshTokenValue) (ret []*targets.Target, removedIds []string, refreshToken RefreshTokenValue, err error)
type TargetRetrievalFunc func(ctx context.Context, addr, authTok string, refreshTok RefreshTokenValue, inPage *targets.TargetListResult, opt ...Option) (ret *targets.TargetListResult, refreshToken RefreshTokenValue, err error)
func defaultTargetFunc(ctx context.Context, addr, authTok string, refreshTok RefreshTokenValue) ([]*targets.Target, []string, RefreshTokenValue, error) {
func defaultTargetFunc(ctx context.Context, addr, authTok string, refreshTok RefreshTokenValue, inPage *targets.TargetListResult, opt ...Option) (*targets.TargetListResult, RefreshTokenValue, error) {
const op = "cache.defaultTargetFunc"
conf, err := api.DefaultConfig()
if err != nil {
return nil, nil, "", errors.Wrap(ctx, err, op)
return nil, "", errors.Wrap(ctx, err, op)
}
opts, err := getOpts(opt...)
if err != nil {
return nil, "", errors.Wrap(ctx, err, op)
}
conf.Addr = addr
conf.Token = authTok
client, err := api.NewClient(conf)
if err != nil {
return nil, nil, "", errors.Wrap(ctx, err, op)
return nil, "", errors.Wrap(ctx, err, op)
}
tarClient := targets.NewClient(client)
l, err := tarClient.List(ctx, "global", targets.WithRecursive(true), targets.WithListToken(string(refreshTok)))
var l *targets.TargetListResult
switch inPage {
case nil:
l, err = tarClient.List(ctx, "global", targets.WithRecursive(true), targets.WithListToken(string(refreshTok)), targets.WithClientDirectedPagination(!opts.withUseNonPagedListing))
default:
l, err = tarClient.ListNextPage(ctx, inPage, targets.WithListToken(string(refreshTok)))
}
if err != nil {
if api.ErrInvalidListToken.Is(err) {
return nil, nil, "", err
return nil, "", err
}
return nil, nil, "", errors.Wrap(ctx, err, op)
return nil, "", errors.Wrap(ctx, err, op)
}
if l.ResponseType == "" {
return nil, nil, "", ErrRefreshNotSupported
return nil, "", ErrRefreshNotSupported
}
return l.Items, l.RemovedIds, RefreshTokenValue(l.ListToken), nil
return l, RefreshTokenValue(l.ListToken), nil
}
// refreshTargets uses attempts to refresh the targets for the provided user
@@ -81,13 +91,13 @@ func (r *Repository) refreshTargets(ctx context.Context, u *user, tokens map[Aut
// Find and use a token for retrieving targets
var gotResponse bool
var resp []*targets.Target
var removedIds []string
var currentPage *targets.TargetListResult
var newRefreshToken RefreshTokenValue
var foundAuthToken string
var unsupportedCacheRequest bool
var retErr error
for at, t := range tokens {
resp, removedIds, newRefreshToken, err = opts.withTargetRetrievalFunc(ctx, u.Address, t, oldRefreshTokenVal)
currentPage, newRefreshToken, err = opts.withTargetRetrievalFunc(ctx, u.Address, t, oldRefreshTokenVal, currentPage)
if api.ErrInvalidListToken.Is(err) {
event.WriteSysEvent(ctx, op, "old list token is no longer valid, starting new initial fetch", "user_id", u.Id)
if err := r.deleteRefreshToken(ctx, u, resourceType); err != nil {
@@ -95,7 +105,7 @@ func (r *Repository) refreshTargets(ctx context.Context, u *user, tokens map[Aut
}
// try again without the refresh token
oldRefreshToken = nil
resp, removedIds, newRefreshToken, err = opts.withTargetRetrievalFunc(ctx, u.Address, t, "")
currentPage, newRefreshToken, err = opts.withTargetRetrievalFunc(ctx, u.Address, t, "", currentPage)
}
if err != nil {
if err == ErrRefreshNotSupported {
@@ -105,6 +115,7 @@ func (r *Repository) refreshTargets(ctx context.Context, u *user, tokens map[Aut
continue
}
}
foundAuthToken = t
gotResponse = true
break
}
@@ -118,44 +129,57 @@ func (r *Repository) refreshTargets(ctx context.Context, u *user, tokens map[Aut
}
var numDeleted int
_, err = r.rw.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error {
var err error
switch {
case oldRefreshToken == nil || unsupportedCacheRequest:
if numDeleted, err = w.Exec(ctx, "delete from target where fk_user_id = @fk_user_id",
[]any{sql.Named("fk_user_id", u.Id)}); err != nil {
return err
var numUpserted int
var clearPerformed bool
for {
_, err = r.rw.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{}, func(_ db.Reader, w db.Writer) error {
var err error
if (oldRefreshToken == nil || unsupportedCacheRequest) && !clearPerformed {
if numDeleted, err = w.Exec(ctx, "delete from target where fk_user_id = @fk_user_id",
[]any{sql.Named("fk_user_id", u.Id)}); err != nil {
return err
}
clearPerformed = true
}
case len(removedIds) > 0:
if numDeleted, err = w.Exec(ctx, "delete from target where id in @ids",
[]any{sql.Named("ids", removedIds)}); err != nil {
return err
switch {
case unsupportedCacheRequest:
if err := upsertRefreshToken(ctx, w, u, resourceType, sentinelNoRefreshToken); err != nil {
return err
}
case newRefreshToken != "":
if err := upsertTargets(ctx, w, u, currentPage.Items); err != nil {
return err
}
numUpserted += len(currentPage.Items)
if err := upsertRefreshToken(ctx, w, u, resourceType, newRefreshToken); err != nil {
return err
}
default:
// controller supports caching, but doesn't have any resources
}
if !unsupportedCacheRequest && len(currentPage.RemovedIds) > 0 {
if numDeleted, err = w.Exec(ctx, "delete from target where id in @ids",
[]any{sql.Named("ids", currentPage.RemovedIds)}); err != nil {
return err
}
}
return nil
})
if unsupportedCacheRequest || currentPage.ResponseType == "" || currentPage.ResponseType == "complete" {
break
}
switch {
case unsupportedCacheRequest:
if err := upsertRefreshToken(ctx, w, u, resourceType, sentinelNoRefreshToken); err != nil {
return err
}
case newRefreshToken != "":
if err := upsertTargets(ctx, w, u, resp); err != nil {
return err
}
if err := upsertRefreshToken(ctx, w, u, resourceType, newRefreshToken); err != nil {
return err
}
default:
// controller supports caching, but doesn't have any resources
currentPage, newRefreshToken, err = opts.withTargetRetrievalFunc(ctx, u.Address, foundAuthToken, newRefreshToken, currentPage)
if err != nil {
break
}
return nil
})
}
if err != nil {
return errors.Wrap(ctx, err, op)
}
if unsupportedCacheRequest {
return ErrRefreshNotSupported
}
event.WriteSysEvent(ctx, op, "targets updated", "deleted", numDeleted, "upserted", len(resp), "user_id", u.Id)
event.WriteSysEvent(ctx, op, "targets updated", "deleted", numDeleted, "upserted", numUpserted, "user_id", u.Id)
return nil
}
@@ -185,12 +209,12 @@ func (r *Repository) checkCachingTargets(ctx context.Context, u *user, tokens ma
// Find and use a token for retrieving targets
var gotResponse bool
var resp []*targets.Target
var resp *targets.TargetListResult
var newRefreshToken RefreshTokenValue
var unsupportedCacheRequest bool
var retErr error
for at, t := range tokens {
resp, _, newRefreshToken, err = opts.withTargetRetrievalFunc(ctx, u.Address, t, "")
resp, newRefreshToken, err = opts.withTargetRetrievalFunc(ctx, u.Address, t, "", nil, WithUseNonPagedListing(true))
if err != nil {
if err == ErrRefreshNotSupported {
unsupportedCacheRequest = true
@@ -221,14 +245,15 @@ func (r *Repository) checkCachingTargets(ctx context.Context, u *user, tokens ma
return err
}
case newRefreshToken != "":
var err error
// Now that there is a refresh token, the data can be cached, so
// cache it and store the refresh token for future refreshes.
// cache it and store the refresh token for future refreshes. First
// remove any values, then add the new ones
var err error
if numDeleted, err = w.Exec(ctx, "delete from target where fk_user_id = @fk_user_id",
[]any{sql.Named("fk_user_id", u.Id)}); err != nil {
return err
}
if err := upsertTargets(ctx, w, u, resp); err != nil {
if err := upsertTargets(ctx, w, u, resp.Items); err != nil {
return err
}
if err := upsertRefreshToken(ctx, w, u, resourceType, newRefreshToken); err != nil {
@@ -249,7 +274,7 @@ func (r *Repository) checkCachingTargets(ctx context.Context, u *user, tokens ma
if unsupportedCacheRequest {
return ErrRefreshNotSupported
}
event.WriteSysEvent(ctx, op, "targets updated", "deleted", numDeleted, "upserted", len(resp), "user_id", u.Id)
event.WriteSysEvent(ctx, op, "targets updated", "deleted", numDeleted, "upserted", len(resp.Items), "user_id", u.Id)
return nil
}
@@ -141,7 +141,7 @@ func TestRepository_refreshTargets(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := r.refreshTargets(ctx, tc.u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{tc.targets}, [][]string{nil})))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{tc.targets}, [][]string{nil}))))
if tc.errorContains == "" {
assert.NoError(t, err)
rw := db.New(s)
@@ -213,12 +213,12 @@ func TestRepository_RefreshTargets_InvalidListTokenError(t *testing.T) {
}
require.NoError(t, r.refreshTargets(ctx, &u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(invalidAuthTokenFunc)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(invalidAuthTokenFunc))))
// This time an invalid auth token should be returned, and refreshTargets should fall back
// to requesting without one.
require.NoError(t, r.refreshTargets(ctx, &u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(invalidAuthTokenFunc)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(invalidAuthTokenFunc))))
assert.Equal(t, 1, withRefreshToken)
assert.Equal(t, 2, withoutRefreshToken)
@@ -261,7 +261,7 @@ func TestRepository_RefreshTargets_withRefreshTokens(t *testing.T) {
}
require.NoError(t, r.refreshTargets(ctx, &u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, ts, [][]string{nil, nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, ts, [][]string{nil, nil})))))
got, err := r.ListTargets(ctx, at.Id)
require.NoError(t, err)
@@ -270,7 +270,7 @@ func TestRepository_RefreshTargets_withRefreshTokens(t *testing.T) {
// Refreshing again uses the refresh token and get additional sessions, appending
// them to the response
require.NoError(t, r.refreshTargets(ctx, &u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, ts, [][]string{nil, nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, ts, [][]string{nil, nil})))))
assert.NoError(t, err)
got, err = r.ListTargets(ctx, at.Id)
@@ -280,7 +280,7 @@ func TestRepository_RefreshTargets_withRefreshTokens(t *testing.T) {
// Refreshing again wont return any more resources, but also none should be
// removed
require.NoError(t, r.refreshTargets(ctx, &u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, ts, [][]string{nil, nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, ts, [][]string{nil, nil})))))
assert.NoError(t, err)
got, err = r.ListTargets(ctx, at.Id)
@@ -289,7 +289,7 @@ func TestRepository_RefreshTargets_withRefreshTokens(t *testing.T) {
// Refresh again with the refresh token being reported as invalid.
require.NoError(t, r.refreshTargets(ctx, &u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testErroringForRefreshTokenRetrievalFunc(t, ts[0]))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testErroringForRefreshTokenRetrievalFunc(t, ts[0])))))
assert.NoError(t, err)
got, err = r.ListTargets(ctx, at.Id)
@@ -345,7 +345,7 @@ func TestRepository_ListTargets(t *testing.T) {
target("3"),
}
require.NoError(t, r.refreshTargets(ctx, u1, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil})))))
t.Run("wrong user gets no targets", func(t *testing.T) {
l, err := r.ListTargets(ctx, kt2.AuthTokenId)
@@ -389,7 +389,7 @@ func TestRepository_ListTargetsLimiting(t *testing.T) {
ts = append(ts, target("t"+strconv.Itoa(i)))
}
require.NoError(t, r.refreshTargets(ctx, u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil})))))
searchService, err := NewSearchService(ctx, r)
require.NoError(t, err)
@@ -512,7 +512,7 @@ func TestRepository_QueryTargets(t *testing.T) {
},
}
require.NoError(t, r.refreshTargets(ctx, u1, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil})))))
t.Run("wrong token gets no targets", func(t *testing.T) {
l, err := r.QueryTargets(ctx, kt2.AuthTokenId, query)
@@ -556,7 +556,7 @@ func TestRepository_QueryTargetsLimiting(t *testing.T) {
ts = append(ts, target("t"+strconv.Itoa(i)))
}
require.NoError(t, r.refreshTargets(ctx, u, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil}))))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil})))))
searchService, err := NewSearchService(ctx, r)
require.NoError(t, err)
@@ -606,13 +606,13 @@ func TestDefaultTargetRetrievalFunc(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, tar2)
got, removed, refTok, err := defaultTargetFunc(tc.Context(), tc.ApiAddrs()[0], tc.Token().Token, "")
got, refTok, err := defaultTargetFunc(tc.Context(), tc.ApiAddrs()[0], tc.Token().Token, "", nil)
assert.NoError(t, err)
assert.NotEmpty(t, refTok)
assert.Empty(t, removed)
assert.Empty(t, got.RemovedIds)
found1 := false
found2 := false
for _, t := range got {
for _, t := range got.Items {
if t.Id == tar1.Item.Id {
found1 = true
}
@@ -623,10 +623,10 @@ func TestDefaultTargetRetrievalFunc(t *testing.T) {
assert.True(t, found1, "expected to find target %s in list", tar1.Item.Id)
assert.True(t, found2, "expected to find target %s in list", tar2.Item.Id)
got2, removed2, refTok2, err := defaultTargetFunc(tc.Context(), tc.ApiAddrs()[0], tc.Token().Token, refTok)
got2, refTok2, err := defaultTargetFunc(tc.Context(), tc.ApiAddrs()[0], tc.Token().Token, refTok, nil)
assert.NoError(t, err)
assert.NotEmpty(t, refTok2)
assert.NotEqual(t, refTok2, refTok)
assert.Empty(t, removed2)
assert.Empty(t, got2)
assert.Empty(t, got.RemovedIds)
assert.Empty(t, got2.Items)
}
+3 -3
View File
@@ -190,11 +190,11 @@ func TestStatus(t *testing.T) {
target("4"),
}
err = r.refreshTargets(ctx, u1, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil})))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts}, [][]string{nil}))))
require.NoError(t, err)
err = r.refreshTargets(ctx, u2, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts[:2]}, [][]string{nil})))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testStaticResourceRetrievalFunc(t, [][]*targets.Target{ts[:2]}, [][]string{nil}))))
require.NoError(t, err)
sess := []*sessions.Session{
@@ -312,7 +312,7 @@ func TestStatus_unsupported(t *testing.T) {
require.ErrorIs(t, err, ErrRefreshNotSupported)
err = r.refreshTargets(ctx, u1, map[AuthToken]string{{Id: "id"}: "something"},
WithTargetRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t)))
WithTargetRetrievalFunc(testTargetStaticResourceRetrievalFunc(testNoRefreshRetrievalFunc[*targets.Target](t))))
require.ErrorIs(t, err, ErrRefreshNotSupported)
err = r.refreshSessions(ctx, u1, map[AuthToken]string{{Id: "id"}: "something"},
@@ -20,9 +20,10 @@ type options struct {
WithReadyToServeNotificationCh chan struct{}
withBoundaryTokenReaderFunc cache.BoundaryTokenReaderFn
withUrl string
withLogger hclog.Logger
withHomeDir string
withUrl string
withLogger hclog.Logger
withHomeDir string
withForceResetSchema bool
}
// Option - how options are passed as args
@@ -110,6 +111,15 @@ func WithBoundaryTokenReaderFunc(_ context.Context, fn cache.BoundaryTokenReader
}
}
// WithForceResetSchema provides an optional way to force resetting the schema,
// e.g. wiping the cache
func WithForceResetSchema(_ context.Context, force bool) Option {
return func(o *options) error {
o.withForceResetSchema = force
return nil
}
}
// WithReadyToServeNotificationCh provides an optional channel to notify when
// the server is ready to serve; mainly used for test timing but exported for
// availability. The channel will be closed just before the HTTP server is
@@ -108,6 +108,14 @@ func Test_GetOpts(t *testing.T) {
testOpts.withHomeDir = "/tmp"
assert.Equal(t, opts, testOpts)
})
t.Run("WithForceResetSchema", func(t *testing.T) {
opts, err := getOpts(WithForceResetSchema(ctx, true))
require.NoError(t, err)
testOpts := getDefaultOptions()
assert.False(t, testOpts.withForceResetSchema)
testOpts.withForceResetSchema = true
assert.Equal(t, opts, testOpts)
})
t.Run("WithReadyToServeNotificationCh", func(t *testing.T) {
ch := make(chan struct{})
opts, err := getOpts(WithReadyToServeNotificationCh(ctx, ch))
@@ -90,6 +90,8 @@ type Config struct {
// The maximum amount of time a refresh should block a search request from
// completing before it times out.
MaxSearchRefreshTimeout time.Duration
// Force resetting the schema, that is, drop all existing data
ForceResetSchema bool
}
func (sc *Config) validate(ctx context.Context) error {
@@ -230,6 +232,7 @@ func (s *CacheServer) Serve(ctx context.Context, cmd Commander, opt ...Option) e
store, err = openStore(ctx,
WithUrl(ctx, s.conf.DatabaseUrl),
WithLogger(ctx, s.logger),
WithForceResetSchema(ctx, s.conf.ForceResetSchema),
)
if err != nil {
return errors.Wrap(ctx, err, op)
@@ -537,6 +540,7 @@ func openStore(ctx context.Context, opt ...Option) (*db.DB, error) {
dbOpts = append(dbOpts, cachedb.WithGormFormatter(opts.withLogger))
}
}
dbOpts = append(dbOpts, cachedb.WithForceResetSchema(opts.withForceResetSchema))
store, err := cachedb.Open(ctx, dbOpts...)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
@@ -97,11 +97,13 @@ func (s *TestServer) AddResources(t *testing.T, p *authtokens.AuthToken, alts []
}
return alts, nil, "addedaliases", nil
}
tarFn := func(ctx context.Context, _, tok string, _ cache.RefreshTokenValue) ([]*targets.Target, []string, cache.RefreshTokenValue, error) {
tarFn := func(ctx context.Context, _ string, tok string, _ cache.RefreshTokenValue, inPage *targets.TargetListResult, opt ...cache.Option) (*targets.TargetListResult, cache.RefreshTokenValue, error) {
if tok != p.Token {
return nil, nil, "", nil
return nil, "", nil
}
return tars, nil, "addedtargets", nil
return &targets.TargetListResult{
Items: tars,
}, "addedtargets", nil
}
sessFn := func(ctx context.Context, _, tok string, _ cache.RefreshTokenValue) ([]*sessions.Session, []string, cache.RefreshTokenValue, error) {
if tok != p.Token {
@@ -124,13 +126,15 @@ func (s *TestServer) AddUnsupportedCachingData(t *testing.T, p *authtokens.AuthT
r, err := cache.NewRepository(ctx, s.CacheServer.store.Load(), &sync.Map{}, s.cmd.ReadTokenFromKeyring, atReadFn)
require.NoError(t, err)
tarFn := func(ctx context.Context, _, tok string, _ cache.RefreshTokenValue) ([]*targets.Target, []string, cache.RefreshTokenValue, error) {
tarFn := func(ctx context.Context, _, tok string, _ cache.RefreshTokenValue, inPage *targets.TargetListResult, opt ...cache.Option) (*targets.TargetListResult, cache.RefreshTokenValue, error) {
if tok != p.Token {
return nil, nil, "", nil
return &targets.TargetListResult{}, "", nil
}
return []*targets.Target{
{Id: "ttcp_unsupported", Name: "unsupported", Description: "not supported"},
}, nil, "", cache.ErrRefreshNotSupported
return &targets.TargetListResult{
Items: []*targets.Target{
{Id: "ttcp_unsupported", Name: "unsupported", Description: "not supported"},
},
}, "", cache.ErrRefreshNotSupported
}
sessFn := func(ctx context.Context, _, tok string, _ cache.RefreshTokenValue) ([]*sessions.Session, []string, cache.RefreshTokenValue, error) {
if tok != p.Token {
+1 -1
View File
@@ -69,7 +69,7 @@ func Open(ctx context.Context, opt ...Option) (*db.DB, error) {
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if !ok {
if !ok || opts.withForceResetSchema {
if err := resetSchema(ctx, conn); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
+14 -5
View File
@@ -9,11 +9,12 @@ import (
)
type options struct {
withSchemaVersion string
withDebug bool
withUrl string
withDbType dbw.DbType
withGormFormatter hclog.Logger
withSchemaVersion string
withDebug bool
withUrl string
withDbType dbw.DbType
withGormFormatter hclog.Logger
withForceResetSchema bool
}
// Option - how options are passed as args
@@ -67,3 +68,11 @@ func WithDebug(debug bool) Option {
return nil
}
}
// WithForceResetSchema provides an optional way to force resetting the cache
func WithForceResetSchema(debug bool) Option {
return func(o *options) error {
o.withForceResetSchema = debug
return nil
}
}
@@ -45,4 +45,12 @@ func Test_GetOpts(t *testing.T) {
testOpts.withSchemaVersion = version
assert.Equal(t, opts, testOpts)
})
t.Run("WithForceResetSchema", func(t *testing.T) {
opts, err := getOpts(WithForceResetSchema(true))
require.NoError(t, err)
testOpts := getDefaultOptions()
assert.False(t, testOpts.withForceResetSchema)
testOpts.withForceResetSchema = true
assert.Equal(t, opts, testOpts)
})
}