Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 092efd4

Browse files
authored
Merge branch 'release/2.21' into cherry-pick-f882e2-release/2.21
2 parents 85bb248 + 3fa1030 commit 092efd4

29 files changed

+1110
-402
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,6 @@ result
7979

8080
# Zed
8181
.zed_server
82+
83+
# dlv debug binaries for go tests
84+
__debug_bin*

cli/ssh_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -1913,7 +1913,9 @@ Expire-Date: 0
19131913
tpty.WriteLine("gpg --list-keys && echo gpg-''-listkeys-command-done")
19141914
listKeysOutput := tpty.ExpectMatch("gpg--listkeys-command-done")
19151915
require.Contains(t, listKeysOutput, "[ultimate] Coder Test <[email protected]>")
1916-
require.Contains(t, listKeysOutput, "[ultimate] Dean Sheather (work key) <[email protected]>")
1916+
// It's fine that this key is expired. We're just testing that the key trust
1917+
// gets synced properly.
1918+
require.Contains(t, listKeysOutput, "[ expired] Dean Sheather (work key) <[email protected]>")
19171919

19181920
// Try to sign something. This demonstrates that the forwarding is
19191921
// working as expected, since the workspace doesn't have access to the

coderd/coderd.go

+68-57
Original file line numberDiff line numberDiff line change
@@ -665,10 +665,11 @@ func New(options *Options) *API {
665665
api.Auditor.Store(&options.Auditor)
666666
api.TailnetCoordinator.Store(&options.TailnetCoordinator)
667667
dialer := &InmemTailnetDialer{
668-
CoordPtr: &api.TailnetCoordinator,
669-
DERPFn: api.DERPMap,
670-
Logger: options.Logger,
671-
ClientID: uuid.New(),
668+
CoordPtr: &api.TailnetCoordinator,
669+
DERPFn: api.DERPMap,
670+
Logger: options.Logger,
671+
ClientID: uuid.New(),
672+
DatabaseHealthCheck: api.Database,
672673
}
673674
stn, err := NewServerTailnet(api.ctx,
674675
options.Logger,
@@ -1147,64 +1148,74 @@ func New(options *Options) *API {
11471148
r.Get("/", api.AssignableSiteRoles)
11481149
})
11491150
r.Route("/{user}", func(r chi.Router) {
1150-
r.Use(httpmw.ExtractUserParam(options.Database))
1151-
r.Post("/convert-login", api.postConvertLoginType)
1152-
r.Delete("/", api.deleteUser)
1153-
r.Get("/", api.userByName)
1154-
r.Get("/autofill-parameters", api.userAutofillParameters)
1155-
r.Get("/login-type", api.userLoginType)
1156-
r.Put("/profile", api.putUserProfile)
1157-
r.Route("/status", func(r chi.Router) {
1158-
r.Put("/suspend", api.putSuspendUserAccount())
1159-
r.Put("/activate", api.putActivateUserAccount())
1151+
r.Group(func(r chi.Router) {
1152+
r.Use(httpmw.ExtractUserParamOptional(options.Database))
1153+
// Creating workspaces does not require permissions on the user, only the
1154+
// organization member. This endpoint should match the authz story of
1155+
// postWorkspacesByOrganization
1156+
r.Post("/workspaces", api.postUserWorkspaces)
11601157
})
1161-
r.Get("/appearance", api.userAppearanceSettings)
1162-
r.Put("/appearance", api.putUserAppearanceSettings)
1163-
r.Route("/password", func(r chi.Router) {
1164-
r.Use(httpmw.RateLimit(options.LoginRateLimit, time.Minute))
1165-
r.Put("/", api.putUserPassword)
1166-
})
1167-
// These roles apply to the site wide permissions.
1168-
r.Put("/roles", api.putUserRoles)
1169-
r.Get("/roles", api.userRoles)
1170-
1171-
r.Route("/keys", func(r chi.Router) {
1172-
r.Post("/", api.postAPIKey)
1173-
r.Route("/tokens", func(r chi.Router) {
1174-
r.Post("/", api.postToken)
1175-
r.Get("/", api.tokens)
1176-
r.Get("/tokenconfig", api.tokenConfig)
1177-
r.Route("/{keyname}", func(r chi.Router) {
1178-
r.Get("/", api.apiKeyByName)
1179-
})
1158+
1159+
r.Group(func(r chi.Router) {
1160+
r.Use(httpmw.ExtractUserParam(options.Database))
1161+
1162+
r.Post("/convert-login", api.postConvertLoginType)
1163+
r.Delete("/", api.deleteUser)
1164+
r.Get("/", api.userByName)
1165+
r.Get("/autofill-parameters", api.userAutofillParameters)
1166+
r.Get("/login-type", api.userLoginType)
1167+
r.Put("/profile", api.putUserProfile)
1168+
r.Route("/status", func(r chi.Router) {
1169+
r.Put("/suspend", api.putSuspendUserAccount())
1170+
r.Put("/activate", api.putActivateUserAccount())
11801171
})
1181-
r.Route("/{keyid}", func(r chi.Router) {
1182-
r.Get("/", api.apiKeyByID)
1183-
r.Delete("/", api.deleteAPIKey)
1172+
r.Get("/appearance", api.userAppearanceSettings)
1173+
r.Put("/appearance", api.putUserAppearanceSettings)
1174+
r.Route("/password", func(r chi.Router) {
1175+
r.Use(httpmw.RateLimit(options.LoginRateLimit, time.Minute))
1176+
r.Put("/", api.putUserPassword)
1177+
})
1178+
// These roles apply to the site wide permissions.
1179+
r.Put("/roles", api.putUserRoles)
1180+
r.Get("/roles", api.userRoles)
1181+
1182+
r.Route("/keys", func(r chi.Router) {
1183+
r.Post("/", api.postAPIKey)
1184+
r.Route("/tokens", func(r chi.Router) {
1185+
r.Post("/", api.postToken)
1186+
r.Get("/", api.tokens)
1187+
r.Get("/tokenconfig", api.tokenConfig)
1188+
r.Route("/{keyname}", func(r chi.Router) {
1189+
r.Get("/", api.apiKeyByName)
1190+
})
1191+
})
1192+
r.Route("/{keyid}", func(r chi.Router) {
1193+
r.Get("/", api.apiKeyByID)
1194+
r.Delete("/", api.deleteAPIKey)
1195+
})
11841196
})
1185-
})
11861197

1187-
r.Route("/organizations", func(r chi.Router) {
1188-
r.Get("/", api.organizationsByUser)
1189-
r.Get("/{organizationname}", api.organizationByUserAndName)
1190-
})
1191-
r.Post("/workspaces", api.postUserWorkspaces)
1192-
r.Route("/workspace/{workspacename}", func(r chi.Router) {
1193-
r.Get("/", api.workspaceByOwnerAndName)
1194-
r.Get("/builds/{buildnumber}", api.workspaceBuildByBuildNumber)
1195-
})
1196-
r.Get("/gitsshkey", api.gitSSHKey)
1197-
r.Put("/gitsshkey", api.regenerateGitSSHKey)
1198-
r.Route("/notifications", func(r chi.Router) {
1199-
r.Route("/preferences", func(r chi.Router) {
1200-
r.Get("/", api.userNotificationPreferences)
1201-
r.Put("/", api.putUserNotificationPreferences)
1198+
r.Route("/organizations", func(r chi.Router) {
1199+
r.Get("/", api.organizationsByUser)
1200+
r.Get("/{organizationname}", api.organizationByUserAndName)
1201+
})
1202+
r.Route("/workspace/{workspacename}", func(r chi.Router) {
1203+
r.Get("/", api.workspaceByOwnerAndName)
1204+
r.Get("/builds/{buildnumber}", api.workspaceBuildByBuildNumber)
1205+
})
1206+
r.Get("/gitsshkey", api.gitSSHKey)
1207+
r.Put("/gitsshkey", api.regenerateGitSSHKey)
1208+
r.Route("/notifications", func(r chi.Router) {
1209+
r.Route("/preferences", func(r chi.Router) {
1210+
r.Get("/", api.userNotificationPreferences)
1211+
r.Put("/", api.putUserNotificationPreferences)
1212+
})
1213+
})
1214+
r.Route("/webpush", func(r chi.Router) {
1215+
r.Post("/subscription", api.postUserWebpushSubscription)
1216+
r.Delete("/subscription", api.deleteUserWebpushSubscription)
1217+
r.Post("/test", api.postUserPushNotificationTest)
12021218
})
1203-
})
1204-
r.Route("/webpush", func(r chi.Router) {
1205-
r.Post("/subscription", api.postUserWebpushSubscription)
1206-
r.Delete("/subscription", api.deleteUserWebpushSubscription)
1207-
r.Post("/test", api.postUserPushNotificationTest)
12081219
})
12091220
})
12101221
})

coderd/coderdtest/authorize.go

+12-6
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func AssertRBAC(t *testing.T, api *coderd.API, client *codersdk.Client) RBACAsse
8181
// Note that duplicate rbac calls are handled by the rbac.Cacher(), but
8282
// will be recorded twice. So AllCalls() returns calls regardless if they
8383
// were returned from the cached or not.
84-
func (a RBACAsserter) AllCalls() []AuthCall {
84+
func (a RBACAsserter) AllCalls() AuthCalls {
8585
return a.Recorder.AllCalls(&a.Subject)
8686
}
8787

@@ -140,8 +140,11 @@ func (a RBACAsserter) Reset() RBACAsserter {
140140
return a
141141
}
142142

143+
type AuthCalls []AuthCall
144+
143145
type AuthCall struct {
144146
rbac.AuthCall
147+
Err error
145148

146149
asserted bool
147150
// callers is a small stack trace for debugging.
@@ -252,7 +255,7 @@ func (r *RecordingAuthorizer) AssertActor(t *testing.T, actor rbac.Subject, did
252255
}
253256

254257
// recordAuthorize is the internal method that records the Authorize() call.
255-
func (r *RecordingAuthorizer) recordAuthorize(subject rbac.Subject, action policy.Action, object rbac.Object) {
258+
func (r *RecordingAuthorizer) recordAuthorize(subject rbac.Subject, action policy.Action, object rbac.Object, authzErr error) {
256259
r.Lock()
257260
defer r.Unlock()
258261

@@ -262,6 +265,7 @@ func (r *RecordingAuthorizer) recordAuthorize(subject rbac.Subject, action polic
262265
Action: action,
263266
Object: object,
264267
},
268+
Err: authzErr,
265269
callers: []string{
266270
// This is a decent stack trace for debugging.
267271
// Some dbauthz calls are a bit nested, so we skip a few.
@@ -288,11 +292,12 @@ func caller(skip int) string {
288292
}
289293

290294
func (r *RecordingAuthorizer) Authorize(ctx context.Context, subject rbac.Subject, action policy.Action, object rbac.Object) error {
291-
r.recordAuthorize(subject, action, object)
292295
if r.Wrapped == nil {
293296
panic("Developer error: RecordingAuthorizer.Wrapped is nil")
294297
}
295-
return r.Wrapped.Authorize(ctx, subject, action, object)
298+
authzErr := r.Wrapped.Authorize(ctx, subject, action, object)
299+
r.recordAuthorize(subject, action, object, authzErr)
300+
return authzErr
296301
}
297302

298303
func (r *RecordingAuthorizer) Prepare(ctx context.Context, subject rbac.Subject, action policy.Action, objectType string) (rbac.PreparedAuthorized, error) {
@@ -339,10 +344,11 @@ func (s *PreparedRecorder) Authorize(ctx context.Context, object rbac.Object) er
339344
s.rw.Lock()
340345
defer s.rw.Unlock()
341346

347+
authzErr := s.prepped.Authorize(ctx, object)
342348
if !s.usingSQL {
343-
s.rec.recordAuthorize(s.subject, s.action, object)
349+
s.rec.recordAuthorize(s.subject, s.action, object, authzErr)
344350
}
345-
return s.prepped.Authorize(ctx, object)
351+
return authzErr
346352
}
347353

348354
func (s *PreparedRecorder) CompileToSQL(ctx context.Context, cfg regosql.ConvertConfig) (string, error) {

coderd/database/dbmem/dbmem.go

+1
Original file line numberDiff line numberDiff line change
@@ -3283,6 +3283,7 @@ func (q *FakeQuerier) GetFailedWorkspaceBuildsByTemplateID(ctx context.Context,
32833283
}
32843284

32853285
workspaceBuildStats = append(workspaceBuildStats, database.GetFailedWorkspaceBuildsByTemplateIDRow{
3286+
WorkspaceID: w.ID,
32863287
WorkspaceName: w.Name,
32873288
WorkspaceOwnerUsername: workspaceOwner.Username,
32883289
TemplateVersionName: templateVersion.Name,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
UPDATE notification_templates
2+
SET
3+
name = 'Report: Workspace Builds Failed For Template',
4+
title_template = E'Workspace builds failed for template "{{.Labels.template_display_name}}"',
5+
body_template = E'Template **{{.Labels.template_display_name}}** has failed to build {{.Data.failed_builds}}/{{.Data.total_builds}} times over the last {{.Data.report_frequency}}.
6+
7+
**Report:**
8+
{{range $version := .Data.template_versions}}
9+
**{{$version.template_version_name}}** failed {{$version.failed_count}} time{{if gt $version.failed_count 1.0}}s{{end}}:
10+
{{range $build := $version.failed_builds}}
11+
* [{{$build.workspace_owner_username}} / {{$build.workspace_name}} / #{{$build.build_number}}]({{base_url}}/@{{$build.workspace_owner_username}}/{{$build.workspace_name}}/builds/{{$build.build_number}})
12+
{{- end}}
13+
{{end}}
14+
We recommend reviewing these issues to ensure future builds are successful.',
15+
actions = '[
16+
{
17+
"label": "View workspaces",
18+
"url": "{{ base_url }}/workspaces?filter=template%3A{{.Labels.template_name}}"
19+
}
20+
]'::jsonb
21+
WHERE id = '34a20db2-e9cc-4a93-b0e4-8569699d7a00';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
UPDATE notification_templates
2+
SET
3+
name = 'Report: Workspace Builds Failed',
4+
title_template = 'Failed workspace builds report',
5+
body_template =
6+
E'The following templates have had build failures over the last {{.Data.report_frequency}}:
7+
{{range $template := .Data.templates}}
8+
- **{{$template.display_name}}** failed to build {{$template.failed_builds}}/{{$template.total_builds}} times
9+
{{end}}
10+
11+
**Report:**
12+
{{range $template := .Data.templates}}
13+
**{{$template.display_name}}**
14+
{{range $version := $template.versions}}
15+
- **{{$version.template_version_name}}** failed {{$version.failed_count}} time{{if gt $version.failed_count 1.0}}s{{end}}:
16+
{{range $build := $version.failed_builds}}
17+
- [{{$build.workspace_owner_username}} / {{$build.workspace_name}} / #{{$build.build_number}}]({{base_url}}/@{{$build.workspace_owner_username}}/{{$build.workspace_name}}/builds/{{$build.build_number}})
18+
{{end}}
19+
{{end}}
20+
{{end}}
21+
22+
We recommend reviewing these issues to ensure future builds are successful.',
23+
actions = '[
24+
{
25+
"label": "View workspaces",
26+
"url": "{{ base_url }}/workspaces?filter={{$first := true}}{{range $template := .Data.templates}}{{range $version := $template.versions}}{{range $build := $version.failed_builds}}{{if not $first}}+{{else}}{{$first = false}}{{end}}id%3A{{$build.workspace_id}}{{end}}{{end}}{{end}}"
27+
}
28+
]'::jsonb
29+
WHERE id = '34a20db2-e9cc-4a93-b0e4-8569699d7a00';

coderd/database/queries.sql.go

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

coderd/database/queries/workspacebuilds.sql

+1
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ SELECT
213213
tv.name AS template_version_name,
214214
u.username AS workspace_owner_username,
215215
w.name AS workspace_name,
216+
w.id AS workspace_id,
216217
wb.build_number AS workspace_build_number
217218
FROM
218219
workspace_build_with_user AS wb

coderd/httpapi/noop.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package httpapi
2+
3+
import "net/http"
4+
5+
// NoopResponseWriter is a response writer that does nothing.
6+
type NoopResponseWriter struct{}
7+
8+
func (NoopResponseWriter) Header() http.Header { return http.Header{} }
9+
func (NoopResponseWriter) Write(p []byte) (int, error) { return len(p), nil }
10+
func (NoopResponseWriter) WriteHeader(int) {}

coderd/httpmw/organizationparam.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func ExtractOrganizationMemberParam(db database.Store) func(http.Handler) http.H
117117
// very important that we do not add the User object to the request context or otherwise
118118
// leak it to the API handler.
119119
// nolint:gocritic
120-
user, ok := extractUserContext(dbauthz.AsSystemRestricted(ctx), db, rw, r)
120+
user, ok := ExtractUserContext(dbauthz.AsSystemRestricted(ctx), db, rw, r)
121121
if !ok {
122122
return
123123
}

0 commit comments

Comments
 (0)