-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathcached_workspace.go
More file actions
82 lines (72 loc) · 2.77 KB
/
cached_workspace.go
File metadata and controls
82 lines (72 loc) · 2.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package agentapi
import (
"context"
"sync"
"github.com/google/uuid"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
)
// CachedWorkspaceFields contains workspace data that is safe to cache for the
// duration of an agent connection. These fields are used to reduce database calls
// in high-frequency operations like stats reporting and metadata updates.
// Prebuild workspaces should not be cached using this struct within the API struct,
// however some of these fields for a workspace can be updated live so there is a
// routine in the API for refreshing the workspace on a timed interval.
//
// IMPORTANT: ACL fields (GroupACL, UserACL) are NOT cached because they can be
// modified in the database and we must use fresh data for authorization checks.
type CachedWorkspaceFields struct {
lock sync.RWMutex
identity database.WorkspaceIdentity
taskID uuid.NullUUID
}
func (cws *CachedWorkspaceFields) Clear() {
cws.lock.Lock()
defer cws.lock.Unlock()
cws.identity = database.WorkspaceIdentity{}
cws.taskID = uuid.NullUUID{}
}
func (cws *CachedWorkspaceFields) UpdateValues(ws database.Workspace) {
cws.lock.Lock()
defer cws.lock.Unlock()
cws.identity.ID = ws.ID
cws.identity.OwnerID = ws.OwnerID
cws.identity.OrganizationID = ws.OrganizationID
cws.identity.TemplateID = ws.TemplateID
cws.identity.Name = ws.Name
cws.identity.OwnerUsername = ws.OwnerUsername
cws.identity.TemplateName = ws.TemplateName
cws.identity.AutostartSchedule = ws.AutostartSchedule
cws.taskID = ws.TaskID
}
func (cws *CachedWorkspaceFields) TaskID() uuid.NullUUID {
cws.lock.RLock()
defer cws.lock.RUnlock()
return cws.taskID
}
// Returns the Workspace, true, unless the workspace has not been cached (nuked or was a prebuild).
func (cws *CachedWorkspaceFields) AsWorkspaceIdentity() (database.WorkspaceIdentity, bool) {
cws.lock.RLock()
defer cws.lock.RUnlock()
// Should we be more explicit about all fields being set to be valid?
if cws.identity.Equal(database.WorkspaceIdentity{}) {
return database.WorkspaceIdentity{}, false
}
return cws.identity, true
}
// ContextInject attempts to inject the rbac object for the cached workspace fields
// into the given context, either returning the wrapped context or the original.
func (cws *CachedWorkspaceFields) ContextInject(ctx context.Context) (context.Context, error) {
var err error
rbacCtx := ctx
if dbws, ok := cws.AsWorkspaceIdentity(); ok {
rbacCtx, err = dbauthz.WithWorkspaceRBAC(ctx, dbws.RBACObject())
if err != nil {
// Don't error level log here, will exit the function. We want to fall back to GetWorkspaceByAgentID.
//nolint:gocritic
return ctx, xerrors.Errorf("Cached workspace was present but RBAC object was invalid: %w", err)
}
}
return rbacCtx, nil
}