Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite89a205

Browse files
committed
feat: notify template admins when prebuild claim results in resource replacement(s)
Signed-off-by: Danny Kopping <dannykopping@gmail.com>
1 parent4bd8305 commite89a205

File tree

11 files changed

+1081
-845
lines changed

11 files changed

+1081
-845
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DELETEFROM notification_templatesWHERE id='89d9745a-816e-4695-a17f-3d0a229e2b8d';
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
INSERT INTO notification_templates
2+
(id, name, title_template, body_template,"group", actions)
3+
VALUES ('89d9745a-816e-4695-a17f-3d0a229e2b8d',
4+
'Prebuilt Workspace Resource Replaced',
5+
E'There might be a problem with a recently claimed prebuilt workspace',
6+
$$
7+
Workspace**{{.Labels.workspace}}** was claimedfrom a prebuilt workspace by**{{.Labels.claimant}}**.
8+
During the claim, Terraform destroyedand recreated the following resources
9+
because oneor more immutable attributes changed:
10+
11+
{{range $resource, $paths := .Data.replacements-}}
12+
- _{{ $resource }}_ was replaced due to changes to _{{ $paths }}_
13+
{{end}}
14+
15+
When Terraform must change an immutable attribute, it replaces the entire resource.
16+
If you’re using prebuilds to speed up provisioning, unexpected replacements will slow down
17+
workspace startup—even when claiming a prebuilt environment.
18+
19+
For tipson preventing replacementsand improving claim performance, see [this guide](https://coder.com/docs/TODO).
20+
$$,
21+
'Workspace Events',
22+
'[
23+
{
24+
"label": "View workspace build",
25+
"url": "{{base_url}}/@{{.Labels.claimant}}/{{.Labels.workspace}}/builds/{{.Labels.workspace_build_num}}"
26+
}
27+
]'::jsonb);

‎coderd/notifications/events.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var (
1818
TemplateWorkspaceManualBuildFailed=uuid.MustParse("2faeee0f-26cb-4e96-821c-85ccb9f71513")
1919
TemplateWorkspaceOutOfMemory=uuid.MustParse("a9d027b4-ac49-4fb1-9f6d-45af15f64e7a")
2020
TemplateWorkspaceOutOfDisk=uuid.MustParse("f047f6a3-5713-40f7-85aa-0394cce9fa3a")
21+
TemplateWorkspaceResourceReplaced=uuid.MustParse("89d9745a-816e-4695-a17f-3d0a229e2b8d")
2122
)
2223

2324
// Account-related events.

‎coderd/provisionerdserver/provisionerdserver.go‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,6 +1728,10 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
17281728
})
17291729
}
17301730

1731+
ifresourceReplacements:=completed.GetWorkspaceBuild().GetResourceReplacements();len(resourceReplacements)>0 {
1732+
s.notifyPrebuiltWorkspaceResourceReplacement(ctx,workspace,workspaceBuild,input.PrebuildClaimedByUser,resourceReplacements)
1733+
}
1734+
17311735
msg,err:=json.Marshal(wspubsub.WorkspaceEvent{
17321736
Kind:wspubsub.WorkspaceEventKindStateChange,
17331737
WorkspaceID:workspace.ID,
@@ -1836,6 +1840,67 @@ func (s *server) notifyWorkspaceDeleted(ctx context.Context, workspace database.
18361840
}
18371841
}
18381842

1843+
func (s*server)notifyPrebuiltWorkspaceResourceReplacement(ctx context.Context,workspace database.Workspace,build database.WorkspaceBuild,claimantID uuid.UUID,replacements []*sdkproto.ResourceReplacements) {
1844+
ifclaimantID==uuid.Nil {
1845+
// This is not a prebuild claim.
1846+
return
1847+
}
1848+
1849+
claimant,err:=s.Database.GetUserByID(ctx,claimantID)
1850+
iferr!=nil {
1851+
s.Logger.Warn(ctx,"failed to find claimant by ID, cannot send prebuilt workspace resource replacement notification",
1852+
slog.F("claimant_id",claimantID.String()),slog.Error(err))
1853+
return
1854+
}
1855+
1856+
templateAdmins,err:=findTemplateAdmins(ctx,s.Database)
1857+
iferr!=nil {
1858+
s.Logger.Warn(ctx,"failed to find template admins, cannot send prebuilt workspace resource replacement notification",
1859+
slog.F("claimant_id",claimantID.String()),slog.Error(err))
1860+
return
1861+
}
1862+
1863+
repls:=make(map[string]string,len(replacements))
1864+
for_,repl:=rangereplacements {
1865+
repls[repl.GetResource()]=strings.Join(repl.GetPaths(),", ")
1866+
}
1867+
1868+
for_,templateAdmin:=rangetemplateAdmins {
1869+
if_,err:=s.NotificationsEnqueuer.EnqueueWithData(ctx,templateAdmin.ID,notifications.TemplateWorkspaceResourceReplaced,
1870+
map[string]string{
1871+
"workspace":workspace.Name,
1872+
"workspace_build_num":fmt.Sprintf("%d",build.BuildNumber),
1873+
"claimant":claimant.Username,
1874+
},
1875+
map[string]any{
1876+
"replacements":repls,
1877+
},"provisionerdserver",
1878+
// Associate this notification with all the related entities.
1879+
workspace.ID,workspace.OwnerID,workspace.TemplateID,workspace.OrganizationID,
1880+
);err!=nil {
1881+
s.Logger.Warn(ctx,"failed to notify of prebuilt workspace resource replacement",slog.Error(err))
1882+
break
1883+
}
1884+
}
1885+
}
1886+
1887+
// findTemplateAdmins fetches all users with template admin permission, including owners.
1888+
funcfindTemplateAdmins(ctx context.Context,store database.Store) ([]database.GetUsersRow,error) {
1889+
owners,err:=store.GetUsers(ctx, database.GetUsersParams{
1890+
RbacRole: []string{codersdk.RoleOwner},
1891+
})
1892+
iferr!=nil {
1893+
returnnil,xerrors.Errorf("get owners: %w",err)
1894+
}
1895+
templateAdmins,err:=store.GetUsers(ctx, database.GetUsersParams{
1896+
RbacRole: []string{codersdk.RoleTemplateAdmin},
1897+
})
1898+
iferr!=nil {
1899+
returnnil,xerrors.Errorf("get template admins: %w",err)
1900+
}
1901+
returnappend(owners,templateAdmins...),nil
1902+
}
1903+
18391904
func (s*server)startTrace(ctx context.Context,namestring,opts...trace.SpanStartOption) (context.Context, trace.Span) {
18401905
returns.Tracer.Start(ctx,name,append(opts,trace.WithAttributes(
18411906
semconv.ServiceNameKey.String("coderd.provisionerd"),

‎provisioner/terraform/executor.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,12 +312,21 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
312312
// the point of prebuilding if the expensive resource is replaced once claimed!
313313
var (
314314
isPrebuildClaimAttempt=!destroy&&metadata.PrebuildClaimForUserId!=""
315+
reps []*proto.ResourceReplacements
315316
)
316317
ifcount:=len(replacements);count>0&&isPrebuildClaimAttempt {
317318
// TODO(dannyk): we should log drift always (not just during prebuild claim attempts); we're validating that this output
318319
// will not be overwhelming for end-users, but it'll certainly be super valuable for template admins
319320
// to diagnose this resource replacement issue, at least.
320321
e.logDrift(ctx,killCtx,planfilePath,logr)
322+
323+
reps=make([]*proto.ResourceReplacements,0,len(replacements))
324+
forn,p:=rangereplacements {
325+
reps=append(reps,&proto.ResourceReplacements{
326+
Resource:n,
327+
Paths:p,
328+
})
329+
}
321330
}
322331

323332
return&proto.PlanComplete{
@@ -327,6 +336,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
327336
Timings:append(e.timings.aggregate(),graphTimings.aggregate()...),
328337
Presets:state.Presets,
329338
Plan:plan,
339+
ResourceReplacements:reps,
330340
},nil
331341
}
332342

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp