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:
1 parent
2a87dcad48
commit
3a4ee032d9
62 files changed
+3875
-1533
No files matched your search
+9
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user