Skip to content

Commit a2ee19d

Browse files
committed
address more review feedback; attempt to simplify cached workspace is
valid/present checks, plus ensure proper fallback (less noisy logging) when a cached workspace is not found/valid in dbauthz Signed-off-by: Callum Styan <callumstyan@gmail.com>
1 parent 089a5ef commit a2ee19d

File tree

4 files changed

+26
-27
lines changed

4 files changed

+26
-27
lines changed

coderd/agentapi/cached_workspace.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,14 @@ func (cws *CachedWorkspaceFields) UpdateValues(ws database.Workspace) {
7373
cws.AutostartSchedule = ws.AutostartSchedule
7474
}
7575

76-
func (cws *CachedWorkspaceFields) AsDatabaseWorkspace() database.Workspace {
76+
// Returns the Workspace, true, unless the workspace has not been cached (nuked or was a prebuild).
77+
func (cws *CachedWorkspaceFields) AsDatabaseWorkspace() (database.Workspace, bool) {
7778
cws.lock.RLock()
7879
defer cws.lock.RUnlock()
80+
// Should we be more explicit about all fields being set to be valid?
81+
if cws.Equal(&CachedWorkspaceFields{}) {
82+
return database.Workspace{}, false
83+
}
7984
return database.Workspace{
8085
ID: cws.ID,
8186
OwnerID: cws.OwnerID,
@@ -85,5 +90,5 @@ func (cws *CachedWorkspaceFields) AsDatabaseWorkspace() database.Workspace {
8590
OwnerUsername: cws.OwnerUsername,
8691
TemplateName: cws.TemplateName,
8792
AutostartSchedule: cws.AutostartSchedule,
88-
}
93+
}, true
8994
}

coderd/agentapi/metadata.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,16 @@ func (a *MetadataAPI) BatchUpdateMetadata(ctx context.Context, req *agentproto.B
111111

112112
// Inject RBAC object into context for dbauthz fast path, avoid having to
113113
// call GetWorkspaceByAgentID on every metadata update.
114-
rbacCtx, err := dbauthz.WithWorkspaceRBAC(ctx, a.Workspace.AsDatabaseWorkspace().RBACObject())
115-
if err != nil {
116-
// Don't use error level here; that will fail the call but in reality an invalid object here is okay
117-
// since we will then just fallback to the actual GetWorkspaceByAgentID call in dbauthz.
118-
a.Log.Warn(ctx, "cached RBAC Workspace context wrapping was invalid", slog.Error(err))
114+
rbacCtx := ctx
115+
if dbws, ok := a.Workspace.AsDatabaseWorkspace(); ok {
116+
rbacCtx, err = dbauthz.WithWorkspaceRBAC(ctx, dbws.RBACObject())
117+
if err != nil {
118+
// Don't error level log here, will exit the function. We want to fall back to GetWorkspaceByAgentID.
119+
//nolint:gocritic
120+
a.Log.Debug(ctx, "Cached workspace was present but RBAC object was invalid", slog.F("err", err))
121+
}
119122
}
123+
120124
err = a.Database.UpdateWorkspaceAgentMetadata(rbacCtx, dbUpdate)
121125
if err != nil {
122126
return nil, xerrors.Errorf("update workspace agent metadata in database: %w", err)

coderd/agentapi/stats.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"time"
66

7-
"github.com/google/uuid"
87
"golang.org/x/xerrors"
98
"google.golang.org/protobuf/types/known/durationpb"
109

@@ -50,8 +49,9 @@ func (a *StatsAPI) UpdateStats(ctx context.Context, req *agentproto.UpdateStatsR
5049
}
5150

5251
// If cache is empty (prebuild or invalid), fall back to DB
53-
ws := a.Workspace.AsDatabaseWorkspace()
54-
if a.Workspace.ID == uuid.Nil {
52+
var ws database.Workspace
53+
var ok bool
54+
if ws, ok = a.Workspace.AsDatabaseWorkspace(); !ok {
5555
ws, err = a.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
5656
if err != nil {
5757
return nil, xerrors.Errorf("get workspace by agent ID %q: %w", workspaceAgent.ID, err)

coderd/database/dbauthz/dbauthz.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5510,25 +5510,15 @@ func (q *querier) UpdateWorkspaceAgentMetadata(ctx context.Context, arg database
55105510
// Fast path: Check if we have an RBAC object in context.
55115511
// This is set by the workspace agent RPC handler to avoid the expensive
55125512
// GetWorkspaceByAgentID query for every metadata update.
5513-
//
5514-
// NOTE: The cached RBAC object is refreshed every 5 minutes. After a prebuild
5515-
// claim, there may be up to a 5-minute window where this uses stale owner_id.
5513+
// NOTE: The cached RBAC object is refreshed every 5 minutes in agentapi/api.go.
55165514
if rbacObj, ok := WorkspaceRBACFromContext(ctx); ok {
5517-
// Verify the RBAC object is valid (has required fields).
5518-
if !rbacObj.IsEmpty() && rbacObj.Type == rbac.ResourceWorkspace.Type {
5519-
act, ok := ActorFromContext(ctx)
5520-
if !ok {
5521-
return ErrNoActor
5522-
}
5523-
// Errors here will result in falling back to the GetWorkspaceAgentByID query, skipping
5524-
// the cache in case the cached data is stale.
5525-
5526-
if err := q.auth.Authorize(ctx, act, policy.ActionUpdate, rbacObj); err == nil {
5527-
return q.db.UpdateWorkspaceAgentMetadata(ctx, arg)
5528-
}
5529-
q.log.Debug(ctx, "fast path authorization failed, using slow path",
5530-
slog.F("agent_id", arg.WorkspaceAgentID))
5515+
// Errors here will result in falling back to the GetWorkspaceAgentByID query, skipping
5516+
// the cache in case the cached data is stale.
5517+
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbacObj); err == nil {
5518+
return q.db.UpdateWorkspaceAgentMetadata(ctx, arg)
55315519
}
5520+
q.log.Debug(ctx, "fast path authorization failed, using slow path",
5521+
slog.F("agent_id", arg.WorkspaceAgentID))
55325522
}
55335523

55345524
// Slow path: Fallback to fetching the workspace for authorization if the RBAC object is not present (or is invalid)

0 commit comments

Comments
 (0)