Skip to content

Commit 06552e2

Browse files
committed
Merge branch 'main' into improve-org-members-table
2 parents 42344ef + 3a614f1 commit 06552e2

38 files changed

+832
-241
lines changed

Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ gen: \
487487
site/src/api/typesGenerated.ts \
488488
coderd/rbac/object_gen.go \
489489
codersdk/rbacresources_gen.go \
490+
site/src/api/rbacresources_gen.ts \
490491
docs/admin/prometheus.md \
491492
docs/cli.md \
492493
docs/admin/audit-logs.md \
@@ -518,6 +519,7 @@ gen/mark-fresh:
518519
site/src/api/typesGenerated.ts \
519520
coderd/rbac/object_gen.go \
520521
codersdk/rbacresources_gen.go \
522+
site/src/api/rbacresources_gen.ts \
521523
docs/admin/prometheus.md \
522524
docs/cli.md \
523525
docs/admin/audit-logs.md \
@@ -622,6 +624,10 @@ coderd/rbac/object_gen.go: scripts/rbacgen/rbacobject.gotmpl scripts/rbacgen/mai
622624
codersdk/rbacresources_gen.go: scripts/rbacgen/codersdk.gotmpl scripts/rbacgen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go
623625
go run scripts/rbacgen/main.go codersdk > codersdk/rbacresources_gen.go
624626

627+
site/src/api/rbacresources_gen.ts: scripts/rbacgen/codersdk.gotmpl scripts/rbacgen/main.go coderd/rbac/object.go coderd/rbac/policy/policy.go
628+
go run scripts/rbacgen/main.go typescript > site/src/api/rbacresources_gen.ts
629+
630+
625631
docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/metrics
626632
go run scripts/metricsdocgen/main.go
627633
./scripts/pnpm_install.sh

cli/organization_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ func TestCurrentOrganization(t *testing.T) {
3232
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
3333
json.NewEncoder(w).Encode([]codersdk.Organization{
3434
{
35-
ID: orgID,
36-
Name: "not-default",
35+
MinimalOrganization: codersdk.MinimalOrganization{
36+
ID: orgID,
37+
Name: "not-default",
38+
},
3739
CreatedAt: time.Now(),
3840
UpdatedAt: time.Now(),
3941
IsDefault: false,

cli/testdata/server-config.yaml.golden

+1-1
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ notifications:
552552
webhook:
553553
# The endpoint to which to send webhooks.
554554
# (default: <unset>, type: url)
555-
hello:
555+
endpoint:
556556
# The upper limit of attempts to send a notification.
557557
# (default: 5, type: int)
558558
maxSendAttempts: 5

cli/vscodessh.go

+42-9
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
151151
// command via the ProxyCommand SSH option.
152152
pid := os.Getppid()
153153

154-
logger := inv.Logger
154+
// Use a stripped down writer that doesn't sync, otherwise you get
155+
// "failed to sync sloghuman: sync /dev/stderr: The handle is
156+
// invalid" on Windows. Syncing isn't required for stdout/stderr
157+
// anyways.
158+
logger := inv.Logger.AppendSinks(sloghuman.Sink(slogWriter{w: inv.Stderr})).Leveled(slog.LevelDebug)
155159
if logDir != "" {
156160
logFilePath := filepath.Join(logDir, fmt.Sprintf("%d.log", pid))
157161
logFile, err := fs.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY, 0o600)
@@ -160,7 +164,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
160164
}
161165
dc := cliutil.DiscardAfterClose(logFile)
162166
defer dc.Close()
163-
logger = logger.AppendSinks(sloghuman.Sink(dc)).Leveled(slog.LevelDebug)
167+
logger = logger.AppendSinks(sloghuman.Sink(dc))
164168
}
165169
if r.disableDirect {
166170
logger.Info(ctx, "direct connections disabled")
@@ -204,31 +208,48 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
204208
// command via the ProxyCommand SSH option.
205209
networkInfoFilePath := filepath.Join(networkInfoDir, fmt.Sprintf("%d.json", pid))
206210

207-
statsErrChan := make(chan error, 1)
211+
var (
212+
firstErrTime time.Time
213+
errCh = make(chan error, 1)
214+
)
208215
cb := func(start, end time.Time, virtual, _ map[netlogtype.Connection]netlogtype.Counts) {
209-
sendErr := func(err error) {
216+
sendErr := func(tolerate bool, err error) {
217+
logger.Error(ctx, "collect network stats", slog.Error(err))
218+
// Tolerate up to 1 minute of errors.
219+
if tolerate {
220+
if firstErrTime.IsZero() {
221+
logger.Info(ctx, "tolerating network stats errors for up to 1 minute")
222+
firstErrTime = time.Now()
223+
}
224+
if time.Since(firstErrTime) < time.Minute {
225+
return
226+
}
227+
}
228+
210229
select {
211-
case statsErrChan <- err:
230+
case errCh <- err:
212231
default:
213232
}
214233
}
215234

216235
stats, err := collectNetworkStats(ctx, agentConn, start, end, virtual)
217236
if err != nil {
218-
sendErr(err)
237+
sendErr(true, err)
219238
return
220239
}
221240

222241
rawStats, err := json.Marshal(stats)
223242
if err != nil {
224-
sendErr(err)
243+
sendErr(false, err)
225244
return
226245
}
227246
err = afero.WriteFile(fs, networkInfoFilePath, rawStats, 0o600)
228247
if err != nil {
229-
sendErr(err)
248+
sendErr(false, err)
230249
return
231250
}
251+
252+
firstErrTime = time.Time{}
232253
}
233254

234255
now := time.Now()
@@ -238,7 +259,7 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
238259
select {
239260
case <-ctx.Done():
240261
return nil
241-
case err := <-statsErrChan:
262+
case err := <-errCh:
242263
return err
243264
}
244265
},
@@ -280,6 +301,18 @@ func (r *RootCmd) vscodeSSH() *serpent.Command {
280301
return cmd
281302
}
282303

304+
// slogWriter wraps an io.Writer and removes all other methods (such as Sync),
305+
// which may cause undesired/broken behavior.
306+
type slogWriter struct {
307+
w io.Writer
308+
}
309+
310+
var _ io.Writer = slogWriter{}
311+
312+
func (s slogWriter) Write(p []byte) (n int, err error) {
313+
return s.w.Write(p)
314+
}
315+
283316
type sshNetworkStats struct {
284317
P2P bool `json:"p2p"`
285318
Latency float64 `json:"latency"`

coderd/apidoc/docs.go

+25
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/audit.go

+16-7
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,6 @@ func (api *API) generateFakeAuditLog(rw http.ResponseWriter, r *http.Request) {
145145
if len(params.AdditionalFields) == 0 {
146146
params.AdditionalFields = json.RawMessage("{}")
147147
}
148-
if params.OrganizationID == uuid.Nil {
149-
params.OrganizationID = uuid.New()
150-
}
151148

152149
_, err = api.Database.InsertAuditLog(ctx, database.InsertAuditLogParams{
153150
ID: uuid.New(),
@@ -241,10 +238,11 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
241238
resourceLink = api.auditLogResourceLink(ctx, dblog, additionalFields)
242239
}
243240

244-
return codersdk.AuditLog{
245-
ID: dblog.ID,
246-
RequestID: dblog.RequestID,
247-
Time: dblog.Time,
241+
alog := codersdk.AuditLog{
242+
ID: dblog.ID,
243+
RequestID: dblog.RequestID,
244+
Time: dblog.Time,
245+
// OrganizationID is deprecated.
248246
OrganizationID: dblog.OrganizationID,
249247
IP: ip,
250248
UserAgent: dblog.UserAgent.String,
@@ -261,6 +259,17 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
261259
ResourceLink: resourceLink,
262260
IsDeleted: isDeleted,
263261
}
262+
263+
if dblog.OrganizationID != uuid.Nil {
264+
alog.Organization = &codersdk.MinimalOrganization{
265+
ID: dblog.OrganizationID,
266+
Name: dblog.OrganizationName,
267+
DisplayName: dblog.OrganizationDisplayName,
268+
Icon: dblog.OrganizationIcon,
269+
}
270+
}
271+
272+
return alog
264273
}
265274

266275
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {

coderd/audit_test.go

+88-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func TestAuditLogs(t *testing.T) {
4646
require.Len(t, alogs.AuditLogs, 1)
4747
})
4848

49-
t.Run("User", func(t *testing.T) {
49+
t.Run("IncludeUser", func(t *testing.T) {
5050
t.Parallel()
5151

5252
ctx := context.Background()
@@ -95,6 +95,92 @@ func TestAuditLogs(t *testing.T) {
9595
require.Equal(t, foundUser, *alogs.AuditLogs[0].User)
9696
})
9797

98+
t.Run("IncludeOrganization", func(t *testing.T) {
99+
t.Parallel()
100+
101+
ctx := context.Background()
102+
client := coderdtest.New(t, nil)
103+
user := coderdtest.CreateFirstUser(t, client)
104+
105+
o, err := client.CreateOrganization(ctx, codersdk.CreateOrganizationRequest{
106+
Name: "new-org",
107+
DisplayName: "New organization",
108+
Description: "A new organization to love and cherish until the test is over.",
109+
Icon: "/emojis/1f48f-1f3ff.png",
110+
})
111+
require.NoError(t, err)
112+
113+
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
114+
OrganizationID: o.ID,
115+
ResourceID: user.UserID,
116+
})
117+
require.NoError(t, err)
118+
119+
alogs, err := client.AuditLogs(ctx, codersdk.AuditLogsRequest{
120+
Pagination: codersdk.Pagination{
121+
Limit: 1,
122+
},
123+
})
124+
require.NoError(t, err)
125+
require.Equal(t, int64(1), alogs.Count)
126+
require.Len(t, alogs.AuditLogs, 1)
127+
128+
// Make sure the organization is fully populated.
129+
require.Equal(t, &codersdk.MinimalOrganization{
130+
ID: o.ID,
131+
Name: o.Name,
132+
DisplayName: o.DisplayName,
133+
Icon: o.Icon,
134+
}, alogs.AuditLogs[0].Organization)
135+
136+
// OrganizationID is deprecated, but make sure it is set.
137+
require.Equal(t, o.ID, alogs.AuditLogs[0].OrganizationID)
138+
139+
// Delete the org and try again, should be mostly empty.
140+
err = client.DeleteOrganization(ctx, o.ID.String())
141+
require.NoError(t, err)
142+
143+
alogs, err = client.AuditLogs(ctx, codersdk.AuditLogsRequest{
144+
Pagination: codersdk.Pagination{
145+
Limit: 1,
146+
},
147+
})
148+
require.NoError(t, err)
149+
require.Equal(t, int64(1), alogs.Count)
150+
require.Len(t, alogs.AuditLogs, 1)
151+
152+
require.Equal(t, &codersdk.MinimalOrganization{
153+
ID: o.ID,
154+
}, alogs.AuditLogs[0].Organization)
155+
156+
// OrganizationID is deprecated, but make sure it is set.
157+
require.Equal(t, o.ID, alogs.AuditLogs[0].OrganizationID)
158+
159+
// Some audit entries do not have an organization at all, in which case the
160+
// response omits the organization.
161+
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
162+
ResourceType: codersdk.ResourceTypeAPIKey,
163+
ResourceID: user.UserID,
164+
})
165+
require.NoError(t, err)
166+
167+
alogs, err = client.AuditLogs(ctx, codersdk.AuditLogsRequest{
168+
SearchQuery: "resource_type:api_key",
169+
Pagination: codersdk.Pagination{
170+
Limit: 1,
171+
},
172+
})
173+
require.NoError(t, err)
174+
require.Equal(t, int64(1), alogs.Count)
175+
require.Len(t, alogs.AuditLogs, 1)
176+
177+
// The other will have no organization.
178+
require.Equal(t, (*codersdk.MinimalOrganization)(nil), alogs.AuditLogs[0].Organization)
179+
180+
// OrganizationID is deprecated, but make sure it is empty.
181+
require.Equal(t, uuid.Nil, alogs.AuditLogs[0].OrganizationID)
182+
})
183+
98184
t.Run("WorkspaceBuildAuditLink", func(t *testing.T) {
99185
t.Parallel()
100186

@@ -159,8 +245,7 @@ func TestAuditLogs(t *testing.T) {
159245

160246
// Add an extra audit log in another organization
161247
err = client.CreateTestAuditLog(ctx, codersdk.CreateTestAuditLogRequest{
162-
ResourceID: owner.UserID,
163-
OrganizationID: uuid.New(),
248+
ResourceID: owner.UserID,
164249
})
165250
require.NoError(t, err)
166251

0 commit comments

Comments
 (0)