Skip to content

Commit 6d7dea9

Browse files
committed
Merge branch 'main' into management-settings
2 parents f86b129 + d8d86b1 commit 6d7dea9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+4758
-1890
lines changed

.github/dependabot.yaml

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ updates:
6161
- dependency-name: "terraform"
6262

6363
- package-ecosystem: "npm"
64-
directory: "/site/"
64+
directories:
65+
- "/site"
66+
- "/offlinedocs"
6567
schedule:
6668
interval: "monthly"
6769
time: "06:00"
@@ -82,33 +84,3 @@ updates:
8284
update-types:
8385
- version-update:semver-major
8486
open-pull-requests-limit: 15
85-
groups:
86-
site:
87-
patterns:
88-
- "*"
89-
90-
- package-ecosystem: "npm"
91-
directory: "/offlinedocs/"
92-
schedule:
93-
interval: "monthly"
94-
time: "06:00"
95-
timezone: "America/Chicago"
96-
reviewers:
97-
- "coder/ts"
98-
commit-message:
99-
prefix: "chore"
100-
labels: []
101-
ignore:
102-
# Ignore patch updates for all dependencies
103-
- dependency-name: "*"
104-
update-types:
105-
- version-update:semver-patch
106-
# Ignore major updates to Node.js types, because they need to
107-
# correspond to the Node.js engine version
108-
- dependency-name: "@types/node"
109-
update-types:
110-
- version-update:semver-major
111-
groups:
112-
offlinedocs:
113-
patterns:
114-
- "*"

.github/workflows/pr-auto-assign.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- name: Assign author
17-
uses: toshimaru/auto-author-assign@v2.1.0
17+
uses: toshimaru/auto-author-assign@v2.1.1

coderd/agentapi/api.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"github.com/coder/coder/v2/coderd/database/pubsub"
2323
"github.com/coder/coder/v2/coderd/externalauth"
2424
"github.com/coder/coder/v2/coderd/prometheusmetrics"
25-
"github.com/coder/coder/v2/coderd/schedule"
2625
"github.com/coder/coder/v2/coderd/tracing"
2726
"github.com/coder/coder/v2/coderd/workspacestats"
2827
"github.com/coder/coder/v2/codersdk"
@@ -60,11 +59,11 @@ type Options struct {
6059
Pubsub pubsub.Pubsub
6160
DerpMapFn func() *tailcfg.DERPMap
6261
TailnetCoordinator *atomic.Pointer[tailnet.Coordinator]
63-
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
6462
StatsReporter *workspacestats.Reporter
6563
AppearanceFetcher *atomic.Pointer[appearance.Fetcher]
6664
PublishWorkspaceUpdateFn func(ctx context.Context, workspaceID uuid.UUID)
6765
PublishWorkspaceAgentLogsUpdateFn func(ctx context.Context, workspaceAgentID uuid.UUID, msg agentsdk.LogsNotifyMessage)
66+
NetworkTelemetryHandler func(batch []*tailnetproto.TelemetryEvent)
6867

6968
AccessURL *url.URL
7069
AppHostname string
@@ -154,10 +153,11 @@ func New(opts Options) *API {
154153
}
155154

156155
api.DRPCService = &tailnet.DRPCService{
157-
CoordPtr: opts.TailnetCoordinator,
158-
Logger: opts.Log,
159-
DerpMapUpdateFrequency: opts.DerpMapUpdateFrequency,
160-
DerpMapFn: opts.DerpMapFn,
156+
CoordPtr: opts.TailnetCoordinator,
157+
Logger: opts.Log,
158+
DerpMapUpdateFrequency: opts.DerpMapUpdateFrequency,
159+
DerpMapFn: opts.DerpMapFn,
160+
NetworkTelemetryHandler: opts.NetworkTelemetryHandler,
161161
}
162162

163163
return api

coderd/audit.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ func (api *API) auditLogs(rw http.ResponseWriter, r *http.Request) {
4545
}
4646

4747
queryStr := r.URL.Query().Get("q")
48-
filter, errs := searchquery.AuditLogs(queryStr)
48+
filter, errs := searchquery.AuditLogs(ctx, api.Database, queryStr)
4949
if len(errs) > 0 {
5050
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
5151
Message: "Invalid audit search query.",
5252
Validations: errs,
5353
})
5454
return
5555
}
56-
filter.Offset = int32(page.Offset)
57-
filter.Limit = int32(page.Limit)
56+
filter.OffsetOpt = int32(page.Offset)
57+
filter.LimitOpt = int32(page.Limit)
5858

5959
if filter.Username == "me" {
6060
filter.UserID = apiKey.UserID

coderd/audit_test.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,48 @@ func TestAuditLogs(t *testing.T) {
177177

178178
// Using the organization selector allows the org admin to fetch audit logs
179179
alogs, err := orgAdmin.AuditLogs(ctx, codersdk.AuditLogsRequest{
180-
SearchQuery: fmt.Sprintf("organization_id:%s", owner.OrganizationID.String()),
180+
SearchQuery: fmt.Sprintf("organization:%s", owner.OrganizationID.String()),
181181
Pagination: codersdk.Pagination{
182182
Limit: 5,
183183
},
184184
})
185185
require.NoError(t, err)
186186
require.Len(t, alogs.AuditLogs, 1)
187+
188+
// Also try fetching by organization name
189+
organization, err := orgAdmin.Organization(ctx, owner.OrganizationID)
190+
require.NoError(t, err)
191+
192+
alogs, err = orgAdmin.AuditLogs(ctx, codersdk.AuditLogsRequest{
193+
SearchQuery: fmt.Sprintf("organization:%s", organization.Name),
194+
Pagination: codersdk.Pagination{
195+
Limit: 5,
196+
},
197+
})
198+
require.NoError(t, err)
199+
require.Len(t, alogs.AuditLogs, 1)
200+
})
201+
202+
t.Run("Organization404", func(t *testing.T) {
203+
t.Parallel()
204+
205+
logger := slogtest.Make(t, &slogtest.Options{
206+
IgnoreErrors: true,
207+
})
208+
ctx := context.Background()
209+
client := coderdtest.New(t, &coderdtest.Options{
210+
Logger: &logger,
211+
})
212+
owner := coderdtest.CreateFirstUser(t, client)
213+
orgAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.ScopedRoleOrgAdmin(owner.OrganizationID))
214+
215+
_, err := orgAdmin.AuditLogs(ctx, codersdk.AuditLogsRequest{
216+
SearchQuery: fmt.Sprintf("organization:%s", "random-name"),
217+
Pagination: codersdk.Pagination{
218+
Limit: 5,
219+
},
220+
})
221+
require.Error(t, err)
187222
})
188223
}
189224

@@ -343,9 +378,6 @@ func TestAuditLogsFilter(t *testing.T) {
343378
t.Parallel()
344379
auditLogs, err := client.AuditLogs(ctx, codersdk.AuditLogsRequest{
345380
SearchQuery: testCase.SearchQuery,
346-
Pagination: codersdk.Pagination{
347-
Limit: 25,
348-
},
349381
})
350382
if testCase.ExpectedError {
351383
require.Error(t, err, "expected error")

coderd/coderd.go

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"cdr.dev/slog"
4040
agentproto "github.com/coder/coder/v2/agent/proto"
4141
"github.com/coder/coder/v2/buildinfo"
42+
"github.com/coder/coder/v2/clock"
4243
_ "github.com/coder/coder/v2/coderd/apidoc" // Used for swagger docs.
4344
"github.com/coder/coder/v2/coderd/appearance"
4445
"github.com/coder/coder/v2/coderd/audit"
@@ -142,14 +143,16 @@ type Options struct {
142143
DERPServer *derp.Server
143144
// BaseDERPMap is used as the base DERP map for all clients and agents.
144145
// Proxies are added to this list.
145-
BaseDERPMap *tailcfg.DERPMap
146-
DERPMapUpdateFrequency time.Duration
147-
SwaggerEndpoint bool
148-
SetUserGroups func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, orgGroupNames map[uuid.UUID][]string, createMissingGroups bool) error
149-
SetUserSiteRoles func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, roles []string) error
150-
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
151-
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
152-
AccessControlStore *atomic.Pointer[dbauthz.AccessControlStore]
146+
BaseDERPMap *tailcfg.DERPMap
147+
DERPMapUpdateFrequency time.Duration
148+
NetworkTelemetryBatchFrequency time.Duration
149+
NetworkTelemetryBatchMaxSize int
150+
SwaggerEndpoint bool
151+
SetUserGroups func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, orgGroupNames map[uuid.UUID][]string, createMissingGroups bool) error
152+
SetUserSiteRoles func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, roles []string) error
153+
TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore]
154+
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
155+
AccessControlStore *atomic.Pointer[dbauthz.AccessControlStore]
153156
// AppSecurityKey is the crypto key used to sign and encrypt tokens related to
154157
// workspace applications. It consists of both a signing and encryption key.
155158
AppSecurityKey workspaceapps.SecurityKey
@@ -305,6 +308,12 @@ func New(options *Options) *API {
305308
if options.DERPMapUpdateFrequency == 0 {
306309
options.DERPMapUpdateFrequency = 5 * time.Second
307310
}
311+
if options.NetworkTelemetryBatchFrequency == 0 {
312+
options.NetworkTelemetryBatchFrequency = 1 * time.Minute
313+
}
314+
if options.NetworkTelemetryBatchMaxSize == 0 {
315+
options.NetworkTelemetryBatchMaxSize = 1_000
316+
}
308317
if options.TailnetCoordinator == nil {
309318
options.TailnetCoordinator = tailnet.NewCoordinator(options.Logger)
310319
}
@@ -539,12 +548,19 @@ func New(options *Options) *API {
539548
if options.DeploymentValues.Prometheus.Enable {
540549
options.PrometheusRegistry.MustRegister(stn)
541550
}
542-
api.TailnetClientService, err = tailnet.NewClientService(
543-
api.Logger.Named("tailnetclient"),
544-
&api.TailnetCoordinator,
545-
api.Options.DERPMapUpdateFrequency,
546-
api.DERPMap,
551+
api.NetworkTelemetryBatcher = tailnet.NewNetworkTelemetryBatcher(
552+
clock.NewReal(),
553+
api.Options.NetworkTelemetryBatchFrequency,
554+
api.Options.NetworkTelemetryBatchMaxSize,
555+
api.handleNetworkTelemetry,
547556
)
557+
api.TailnetClientService, err = tailnet.NewClientService(tailnet.ClientServiceOptions{
558+
Logger: api.Logger.Named("tailnetclient"),
559+
CoordPtr: &api.TailnetCoordinator,
560+
DERPMapUpdateFrequency: api.Options.DERPMapUpdateFrequency,
561+
DERPMapFn: api.DERPMap,
562+
NetworkTelemetryHandler: api.NetworkTelemetryBatcher.Handler,
563+
})
548564
if err != nil {
549565
api.Logger.Fatal(api.ctx, "failed to initialize tailnet client service", slog.Error(err))
550566
}
@@ -1255,6 +1271,7 @@ type API struct {
12551271
Auditor atomic.Pointer[audit.Auditor]
12561272
WorkspaceClientCoordinateOverride atomic.Pointer[func(rw http.ResponseWriter) bool]
12571273
TailnetCoordinator atomic.Pointer[tailnet.Coordinator]
1274+
NetworkTelemetryBatcher *tailnet.NetworkTelemetryBatcher
12581275
TailnetClientService *tailnet.ClientService
12591276
QuotaCommitter atomic.Pointer[proto.QuotaCommitter]
12601277
AppearanceFetcher atomic.Pointer[appearance.Fetcher]
@@ -1313,7 +1330,12 @@ type API struct {
13131330

13141331
// Close waits for all WebSocket connections to drain before returning.
13151332
func (api *API) Close() error {
1316-
api.cancel()
1333+
select {
1334+
case <-api.ctx.Done():
1335+
return xerrors.New("API already closed")
1336+
default:
1337+
api.cancel()
1338+
}
13171339
if api.derpCloseFunc != nil {
13181340
api.derpCloseFunc()
13191341
}
@@ -1348,6 +1370,7 @@ func (api *API) Close() error {
13481370
}
13491371
_ = api.agentProvider.Close()
13501372
_ = api.statsReporter.Close()
1373+
_ = api.NetworkTelemetryBatcher.Close()
13511374
return nil
13521375
}
13531376

coderd/database/dbauthz/dbauthz.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,13 @@ func (q *querier) AcquireLock(ctx context.Context, id int64) error {
817817
return q.db.AcquireLock(ctx, id)
818818
}
819819

820+
func (q *querier) AcquireNotificationMessages(ctx context.Context, arg database.AcquireNotificationMessagesParams) ([]database.AcquireNotificationMessagesRow, error) {
821+
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
822+
return nil, err
823+
}
824+
return q.db.AcquireNotificationMessages(ctx, arg)
825+
}
826+
820827
// TODO: We need to create a ProvisionerJob resource type
821828
func (q *querier) AcquireProvisionerJob(ctx context.Context, arg database.AcquireProvisionerJobParams) (database.ProvisionerJob, error) {
822829
// if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
@@ -861,6 +868,20 @@ func (q *querier) BatchUpdateWorkspaceLastUsedAt(ctx context.Context, arg databa
861868
return q.db.BatchUpdateWorkspaceLastUsedAt(ctx, arg)
862869
}
863870

871+
func (q *querier) BulkMarkNotificationMessagesFailed(ctx context.Context, arg database.BulkMarkNotificationMessagesFailedParams) (int64, error) {
872+
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
873+
return 0, err
874+
}
875+
return q.db.BulkMarkNotificationMessagesFailed(ctx, arg)
876+
}
877+
878+
func (q *querier) BulkMarkNotificationMessagesSent(ctx context.Context, arg database.BulkMarkNotificationMessagesSentParams) (int64, error) {
879+
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
880+
return 0, err
881+
}
882+
return q.db.BulkMarkNotificationMessagesSent(ctx, arg)
883+
}
884+
864885
func (q *querier) CleanTailnetCoordinators(ctx context.Context) error {
865886
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceTailnetCoordinator); err != nil {
866887
return err
@@ -1010,6 +1031,13 @@ func (q *querier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Contex
10101031
return q.db.DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx, arg)
10111032
}
10121033

1034+
func (q *querier) DeleteOldNotificationMessages(ctx context.Context) error {
1035+
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil {
1036+
return err
1037+
}
1038+
return q.db.DeleteOldNotificationMessages(ctx)
1039+
}
1040+
10131041
func (q *querier) DeleteOldProvisionerDaemons(ctx context.Context) error {
10141042
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceSystem); err != nil {
10151043
return err
@@ -1114,13 +1142,27 @@ func (q *querier) DeleteWorkspaceAgentPortSharesByTemplate(ctx context.Context,
11141142
return q.db.DeleteWorkspaceAgentPortSharesByTemplate(ctx, templateID)
11151143
}
11161144

1145+
func (q *querier) EnqueueNotificationMessage(ctx context.Context, arg database.EnqueueNotificationMessageParams) (database.NotificationMessage, error) {
1146+
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
1147+
return database.NotificationMessage{}, err
1148+
}
1149+
return q.db.EnqueueNotificationMessage(ctx, arg)
1150+
}
1151+
11171152
func (q *querier) FavoriteWorkspace(ctx context.Context, id uuid.UUID) error {
11181153
fetch := func(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
11191154
return q.db.GetWorkspaceByID(ctx, id)
11201155
}
11211156
return update(q.log, q.auth, fetch, q.db.FavoriteWorkspace)(ctx, id)
11221157
}
11231158

1159+
func (q *querier) FetchNewMessageMetadata(ctx context.Context, arg database.FetchNewMessageMetadataParams) (database.FetchNewMessageMetadataRow, error) {
1160+
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
1161+
return database.FetchNewMessageMetadataRow{}, err
1162+
}
1163+
return q.db.FetchNewMessageMetadata(ctx, arg)
1164+
}
1165+
11241166
func (q *querier) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) {
11251167
return fetch(q.log, q.auth, q.db.GetAPIKeyByID)(ctx, id)
11261168
}

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ func (s *MethodTestSuite) TestAuditLogs() {
263263
_ = dbgen.AuditLog(s.T(), db, database.AuditLog{})
264264
_ = dbgen.AuditLog(s.T(), db, database.AuditLog{})
265265
check.Args(database.GetAuditLogsOffsetParams{
266-
Limit: 10,
266+
LimitOpt: 10,
267267
}).Asserts(rbac.ResourceAuditLog, policy.ActionRead)
268268
}))
269269
}
@@ -2467,6 +2467,32 @@ func (s *MethodTestSuite) TestSystemFunctions() {
24672467
AgentID: uuid.New(),
24682468
}).Asserts(tpl, policy.ActionCreate)
24692469
}))
2470+
s.Run("AcquireNotificationMessages", s.Subtest(func(db database.Store, check *expects) {
2471+
// TODO: update this test once we have a specific role for notifications
2472+
check.Args(database.AcquireNotificationMessagesParams{}).Asserts(rbac.ResourceSystem, policy.ActionUpdate)
2473+
}))
2474+
s.Run("BulkMarkNotificationMessagesFailed", s.Subtest(func(db database.Store, check *expects) {
2475+
// TODO: update this test once we have a specific role for notifications
2476+
check.Args(database.BulkMarkNotificationMessagesFailedParams{}).Asserts(rbac.ResourceSystem, policy.ActionUpdate)
2477+
}))
2478+
s.Run("BulkMarkNotificationMessagesSent", s.Subtest(func(db database.Store, check *expects) {
2479+
// TODO: update this test once we have a specific role for notifications
2480+
check.Args(database.BulkMarkNotificationMessagesSentParams{}).Asserts(rbac.ResourceSystem, policy.ActionUpdate)
2481+
}))
2482+
s.Run("DeleteOldNotificationMessages", s.Subtest(func(db database.Store, check *expects) {
2483+
// TODO: update this test once we have a specific role for notifications
2484+
check.Args().Asserts(rbac.ResourceSystem, policy.ActionDelete)
2485+
}))
2486+
s.Run("EnqueueNotificationMessage", s.Subtest(func(db database.Store, check *expects) {
2487+
// TODO: update this test once we have a specific role for notifications
2488+
check.Args(database.EnqueueNotificationMessageParams{
2489+
Method: database.NotificationMethodWebhook,
2490+
}).Asserts(rbac.ResourceSystem, policy.ActionCreate)
2491+
}))
2492+
s.Run("FetchNewMessageMetadata", s.Subtest(func(db database.Store, check *expects) {
2493+
// TODO: update this test once we have a specific role for notifications
2494+
check.Args(database.FetchNewMessageMetadataParams{}).Asserts(rbac.ResourceSystem, policy.ActionRead)
2495+
}))
24702496
}
24712497

24722498
func (s *MethodTestSuite) TestOAuth2ProviderApps() {

coderd/database/dbgen/dbgen_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestGenerator(t *testing.T) {
1919
t.Parallel()
2020
db := dbmem.New()
2121
_ = dbgen.AuditLog(t, db, database.AuditLog{})
22-
logs := must(db.GetAuditLogsOffset(context.Background(), database.GetAuditLogsOffsetParams{Limit: 1}))
22+
logs := must(db.GetAuditLogsOffset(context.Background(), database.GetAuditLogsOffsetParams{LimitOpt: 1}))
2323
require.Len(t, logs, 1)
2424
})
2525

0 commit comments

Comments
 (0)