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

Skip to content

Commit 4859410

Browse files
committed
feat: add resource replacement notification
Signed-off-by: Danny Kopping <[email protected]>
1 parent 192f75f commit 4859410

26 files changed

+320
-136
lines changed

coderd/coderd.go

+6
Original file line numberDiff line numberDiff line change
@@ -1763,6 +1763,11 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
17631763
return nil, xerrors.Errorf("failed to create in-memory provisioner daemon: %w", err)
17641764
}
17651765

1766+
var prebuildsOrchestrator prebuilds.ReconciliationOrchestrator
1767+
if val := api.PrebuildsReconciler.Load(); val != nil {
1768+
prebuildsOrchestrator = *val
1769+
}
1770+
17661771
mux := drpcmux.New()
17671772
api.Logger.Debug(dialCtx, "starting in-memory provisioner daemon", slog.F("name", name))
17681773
logger := api.Logger.Named(fmt.Sprintf("inmem-provisionerd-%s", name))
@@ -1790,6 +1795,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
17901795
Clock: api.Clock,
17911796
},
17921797
api.NotificationsEnqueuer,
1798+
prebuildsOrchestrator,
17931799
)
17941800
if err != nil {
17951801
return nil, err

coderd/database/dbauthz/dbauthz.go

+7
Original file line numberDiff line numberDiff line change
@@ -2524,6 +2524,13 @@ func (q *querier) GetTemplateParameterInsights(ctx context.Context, arg database
25242524
return q.db.GetTemplateParameterInsights(ctx, arg)
25252525
}
25262526

2527+
func (q *querier) GetTemplatePresetsByID(ctx context.Context, id uuid.UUID) (database.TemplateVersionPreset, error) {
2528+
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceTemplate.All()); err != nil {
2529+
return database.TemplateVersionPreset{}, err
2530+
}
2531+
return q.db.GetTemplatePresetsByID(ctx, id)
2532+
}
2533+
25272534
func (q *querier) GetTemplatePresetsWithPrebuilds(ctx context.Context, templateID uuid.NullUUID) ([]database.GetTemplatePresetsWithPrebuildsRow, error) {
25282535
// GetTemplatePresetsWithPrebuilds retrieves template versions with configured presets and prebuilds.
25292536
// Presets and prebuilds are part of the template, so if you can access templates - you can access them as well.

coderd/database/dbauthz/dbauthz_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -4873,6 +4873,27 @@ func (s *MethodTestSuite) TestPrebuilds() {
48734873
Asserts(rbac.ResourceTemplate.All(), policy.ActionRead).
48744874
ErrorsWithInMemDB(dbmem.ErrUnimplemented)
48754875
}))
4876+
s.Run("GetTemplatePresetsByID", s.Subtest(func(db database.Store, check *expects) {
4877+
org := dbgen.Organization(s.T(), db, database.Organization{})
4878+
user := dbgen.User(s.T(), db, database.User{})
4879+
t := dbgen.Template(s.T(), db, database.Template{
4880+
OrganizationID: org.ID,
4881+
CreatedBy: user.ID,
4882+
})
4883+
tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{
4884+
TemplateID: uuid.NullUUID{UUID: t.ID, Valid: true},
4885+
OrganizationID: org.ID,
4886+
CreatedBy: user.ID,
4887+
})
4888+
preset := dbgen.Preset(s.T(), db, database.InsertPresetParams{
4889+
TemplateVersionID: tv.ID,
4890+
Name: "my-preset",
4891+
DesiredInstances: sql.NullInt32{Int32: 1, Valid: true},
4892+
})
4893+
check.Args(preset.ID).
4894+
Asserts(rbac.ResourceTemplate.All(), policy.ActionRead).
4895+
ErrorsWithInMemDB(dbmem.ErrUnimplemented)
4896+
}))
48764897
s.Run("GetPresetByID", s.Subtest(func(db database.Store, check *expects) {
48774898
org := dbgen.Organization(s.T(), db, database.Organization{})
48784899
user := dbgen.User(s.T(), db, database.User{})

coderd/database/dbmem/dbmem.go

+4
Original file line numberDiff line numberDiff line change
@@ -6072,6 +6072,10 @@ func (q *FakeQuerier) GetTemplateParameterInsights(ctx context.Context, arg data
60726072
return rows, nil
60736073
}
60746074

6075+
func (q *FakeQuerier) GetTemplatePresetsByID(ctx context.Context, id uuid.UUID) (database.TemplateVersionPreset, error) {
6076+
return database.TemplateVersionPreset{}, ErrUnimplemented
6077+
}
6078+
60756079
func (*FakeQuerier) GetTemplatePresetsWithPrebuilds(_ context.Context, _ uuid.NullUUID) ([]database.GetTemplatePresetsWithPrebuildsRow, error) {
60766080
return nil, ErrUnimplemented
60776081
}

coderd/database/dbmetrics/querymetrics.go

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

coderd/database/dbmock/dbmock.go

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

coderd/database/migrations/000320_resource_replacements_notification.up.sql

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ VALUES ('89d9745a-816e-4695-a17f-3d0a229e2b8d',
55
E'There might be a problem with a recently claimed prebuilt workspace',
66
$$
77
Workspace **{{.Labels.workspace}}** was claimed from a prebuilt workspace by **{{.Labels.claimant}}**.
8+
89
During the claim, Terraform destroyed and recreated the following resources
910
because one or more immutable attributes changed:
1011

@@ -16,12 +17,18 @@ When Terraform must change an immutable attribute, it replaces the entire resour
1617
If you’re using prebuilds to speed up provisioning, unexpected replacements will slow down
1718
workspace startup—even when claiming a prebuilt environment.
1819

19-
For tips on preventing replacements and improving claim performance, see [this guide](https://coder.com/docs/TODO).
20+
For tips on preventing replacements and improving claim performance, see [this guide](https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces.md#preventing-resource-replacement).
21+
22+
NOTE: this prebuilt workspace used the **{{.Labels.preset}}** preset.
2023
$$,
21-
'Workspace Events',
24+
'Template Events',
2225
'[
2326
{
2427
"label": "View workspace build",
2528
"url": "{{base_url}}/@{{.Labels.claimant}}/{{.Labels.workspace}}/builds/{{.Labels.workspace_build_num}}"
29+
},
30+
{
31+
"label": "View template version",
32+
"url": "{{base_url}}/templates/{{.Labels.org}}/{{.Labels.template}}/versions/{{.Labels.template_version}}"
2633
}
2734
]'::jsonb);

coderd/database/querier.go

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

coderd/database/queries.sql.go

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

coderd/database/queries/prebuilds.sql

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ FROM templates t
4242
WHERE tvp.desired_instances IS NOT NULL -- Consider only presets that have a prebuild configuration.
4343
AND (t.id = sqlc.narg('template_id')::uuid OR sqlc.narg('template_id') IS NULL);
4444

45+
-- name: GetTemplatePresetsByID :one
46+
SELECT *
47+
FROM template_version_presets
48+
WHERE id = $1;
49+
4550
-- name: GetRunningPrebuiltWorkspaces :many
4651
SELECT
4752
p.id,

coderd/notifications/enqueuer.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ type StoreEnqueuer struct {
5050
}
5151

5252
// NewStoreEnqueuer creates an Enqueuer implementation which can persist notification messages in the store.
53-
func NewStoreEnqueuer(cfg codersdk.NotificationsConfig, store Store, helpers template.FuncMap, log slog.Logger, clock quartz.Clock) (*StoreEnqueuer, error) {
53+
func NewStoreEnqueuer(cfg codersdk.NotificationsConfig, store Store, helpers template.FuncMap, log slog.Logger, clock quartz.Clock) (Enqueuer, error) {
5454
var method database.NotificationMethod
5555
// TODO(DanielleMaywood):
5656
// Currently we do not want to allow setting `inbox` as the default notification method.
@@ -203,7 +203,7 @@ func (s *StoreEnqueuer) buildPayload(metadata database.FetchNewMessageMetadataRo
203203
type NoopEnqueuer struct{}
204204

205205
// NewNoopEnqueuer builds a NoopEnqueuer which is used to fulfill the contract for enqueuing notifications, if ExperimentNotifications is not set.
206-
func NewNoopEnqueuer() *NoopEnqueuer {
206+
func NewNoopEnqueuer() Enqueuer {
207207
return &NoopEnqueuer{}
208208
}
209209

coderd/notifications/events.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ 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")
2221
)
2322

2423
// Account-related events.
@@ -40,6 +39,7 @@ var (
4039
TemplateTemplateDeprecated = uuid.MustParse("f40fae84-55a2-42cd-99fa-b41c1ca64894")
4140

4241
TemplateWorkspaceBuildsFailedReport = uuid.MustParse("34a20db2-e9cc-4a93-b0e4-8569699d7a00")
42+
TemplateWorkspaceResourceReplaced = uuid.MustParse("89d9745a-816e-4695-a17f-3d0a229e2b8d")
4343
)
4444

4545
// Notification-related events.

coderd/notifications/notifications_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -1235,8 +1235,12 @@ func TestNotificationTemplates_Golden(t *testing.T) {
12351235
UserEmail: "[email protected]",
12361236
UserUsername: "bobby",
12371237
Labels: map[string]string{
1238+
"org": "cern",
12381239
"workspace": "my-workspace",
12391240
"workspace_build_num": "2",
1241+
"template": "docker",
1242+
"template_version": "angry_torvalds",
1243+
"preset": "particle-accelerator",
12401244
"claimant": "prebuilds-claimer",
12411245
},
12421246
Data: map[string]any{

coderd/notifications/testdata/rendered-templates/smtp/TemplateWorkspaceResourceReplaced.html.golden

+24-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Hi Bobby,
1414

1515
Workspace my-workspace was claimed from a prebuilt workspace by prebuilds-c=
1616
laimer.
17+
1718
During the claim, Terraform destroyed and recreated the following resources
1819
because one or more immutable attributes changed:
1920

@@ -26,12 +27,18 @@ acements will slow down
2627
workspace startup=E2=80=94even when claiming a prebuilt environment.
2728

2829
For tips on preventing replacements and improving claim performance, see th=
29-
is guide (https://coder.com/docs/TODO).
30+
is guide (https://coder.com/docs/admin/templates/extending-templates/prebui=
31+
lt-workspaces.md#preventing-resource-replacement).
32+
33+
NOTE: this prebuilt workspace used the particle-accelerator preset.
3034

3135

3236
View workspace build: http://test.com/@prebuilds-claimer/my-workspace/build=
3337
s/2
3438

39+
View template version: http://test.com/templates/cern/docker/versions/angry=
40+
_torvalds
41+
3542
--bbe61b741255b6098bb6b3c1f41b885773df633cb18d2a3002b68e4bc9c4
3643
Content-Transfer-Encoding: quoted-printable
3744
Content-Type: text/html; charset=UTF-8
@@ -63,9 +70,10 @@ argin: 8px 0 32px; line-height: 1.5;">
6370
<div style=3D"line-height: 1.5;">
6471
<p>Hi Bobby,</p>
6572
<p>Workspace <strong>my-workspace</strong> was claimed from a prebu=
66-
ilt workspace by <strong>prebuilds-claimer</strong>.<br>
67-
During the claim, Terraform destroyed and recreated the following resources=
68-
<br>
73+
ilt workspace by <strong>prebuilds-claimer</strong>.</p>
74+
75+
<p>During the claim, Terraform destroyed and recreated the following resour=
76+
ces<br>
6977
because one or more immutable attributes changed:</p>
7078

7179
<ul>
@@ -81,7 +89,11 @@ acements will slow down<br>
8189
workspace startup=E2=80=94even when claiming a prebuilt environment.</p>
8290

8391
<p>For tips on preventing replacements and improving claim performance, see=
84-
<a href=3D"https://coder.com/docs/TODO">this guide</a>.</p>
92+
<a href=3D"https://coder.com/docs/admin/templates/extending-templates/preb=
93+
uilt-workspaces.md#preventing-resource-replacement">this guide</a>.</p>
94+
95+
<p>NOTE: this prebuilt workspace used the <strong>particle-accelerator</str=
96+
ong> preset.</p>
8597
</div>
8698
<div style=3D"text-align: center; margin-top: 32px;">
8799
=20
@@ -92,6 +104,13 @@ workspace startup=E2=80=94even when claiming a prebuilt environment.</p>
92104
View workspace build
93105
</a>
94106
=20
107+
<a href=3D"http://test.com/templates/cern/docker/versions/angry_tor=
108+
valds" style=3D"display: inline-block; padding: 13px 24px; background-color=
109+
: #020617; color: #f8fafc; text-decoration: none; border-radius: 8px; margi=
110+
n: 0 4px;">
111+
View template version
112+
</a>
113+
=20
95114
</div>
96115
<div style=3D"border-top: 1px solid #e2e8f0; color: #475569; font-siz=
97116
e: 12px; margin-top: 64px; padding-top: 24px; line-height: 1.6;">

coderd/notifications/testdata/rendered-templates/webhook/TemplateWorkspaceResourceReplaced.json.golden

+10-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,18 @@
1313
{
1414
"label": "View workspace build",
1515
"url": "http://test.com/@prebuilds-claimer/my-workspace/builds/2"
16+
},
17+
{
18+
"label": "View template version",
19+
"url": "http://test.com/templates/cern/docker/versions/angry_torvalds"
1620
}
1721
],
1822
"labels": {
1923
"claimant": "prebuilds-claimer",
24+
"org": "cern",
25+
"preset": "particle-accelerator",
26+
"template": "docker",
27+
"template_version": "angry_torvalds",
2028
"workspace": "my-workspace",
2129
"workspace_build_num": "2"
2230
},
@@ -29,6 +37,6 @@
2937
},
3038
"title": "There might be a problem with a recently claimed prebuilt workspace",
3139
"title_markdown": "There might be a problem with a recently claimed prebuilt workspace",
32-
"body": "Workspace my-workspace was claimed from a prebuilt workspace by prebuilds-claimer.\nDuring the claim, Terraform destroyed and recreated the following resources\nbecause one or more immutable attributes changed:\n\ndocker_container[0] was replaced due to changes to env, hostname\n\nWhen Terraform must change an immutable attribute, it replaces the entire resource.\nIf you’re using prebuilds to speed up provisioning, unexpected replacements will slow down\nworkspace startup—even when claiming a prebuilt environment.\n\nFor tips on preventing replacements and improving claim performance, see this guide (https://coder.com/docs/TODO).",
33-
"body_markdown": "\nWorkspace **my-workspace** was claimed from a prebuilt workspace by **prebuilds-claimer**.\nDuring the claim, Terraform destroyed and recreated the following resources\nbecause one or more immutable attributes changed:\n\n- _docker_container[0]_ was replaced due to changes to _env, hostname_\n\n\nWhen Terraform must change an immutable attribute, it replaces the entire resource.\nIf you’re using prebuilds to speed up provisioning, unexpected replacements will slow down\nworkspace startup—even when claiming a prebuilt environment.\n\nFor tips on preventing replacements and improving claim performance, see [this guide](https://coder.com/docs/TODO).\n"
40+
"body": "Workspace my-workspace was claimed from a prebuilt workspace by prebuilds-claimer.\n\nDuring the claim, Terraform destroyed and recreated the following resources\nbecause one or more immutable attributes changed:\n\ndocker_container[0] was replaced due to changes to env, hostname\n\nWhen Terraform must change an immutable attribute, it replaces the entire resource.\nIf you’re using prebuilds to speed up provisioning, unexpected replacements will slow down\nworkspace startup—even when claiming a prebuilt environment.\n\nFor tips on preventing replacements and improving claim performance, see this guide (https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces.md#preventing-resource-replacement).\n\nNOTE: this prebuilt workspace used the particle-accelerator preset.",
41+
"body_markdown": "\nWorkspace **my-workspace** was claimed from a prebuilt workspace by **prebuilds-claimer**.\n\nDuring the claim, Terraform destroyed and recreated the following resources\nbecause one or more immutable attributes changed:\n\n- _docker_container[0]_ was replaced due to changes to _env, hostname_\n\n\nWhen Terraform must change an immutable attribute, it replaces the entire resource.\nIf you’re using prebuilds to speed up provisioning, unexpected replacements will slow down\nworkspace startup—even when claiming a prebuilt environment.\n\nFor tips on preventing replacements and improving claim performance, see [this guide](https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces.md#preventing-resource-replacement).\n\nNOTE: this prebuilt workspace used the **particle-accelerator** preset.\n"
3442
}

coderd/prebuilds/api.go

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"golang.org/x/xerrors"
88

99
"github.com/coder/coder/v2/coderd/database"
10+
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
1011
)
1112

1213
var (
@@ -27,6 +28,10 @@ type ReconciliationOrchestrator interface {
2728
// Stop gracefully shuts down the orchestrator with the given cause.
2829
// The cause is used for logging and error reporting.
2930
Stop(ctx context.Context, cause error)
31+
32+
// TrackResourceReplacement handles a pathological situation whereby a terraform resource is replaced due to drift,
33+
// which can obviate the whole point of pre-provisioning a prebuilt workspace.
34+
TrackResourceReplacement(ctx context.Context, workspaceID, buildID, claimantID uuid.UUID, replacements []*sdkproto.ResourceReplacement)
3035
}
3136

3237
type Reconciler interface {

coderd/prebuilds/noop.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ import (
66
"github.com/google/uuid"
77

88
"github.com/coder/coder/v2/coderd/database"
9+
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
910
)
1011

1112
type NoopReconciler struct{}
1213

13-
func (NoopReconciler) Run(context.Context) {}
14-
func (NoopReconciler) Stop(context.Context, error) {}
14+
func (NoopReconciler) Run(context.Context) {}
15+
func (NoopReconciler) Stop(context.Context, error) {}
16+
func (NoopReconciler) TrackResourceReplacement(ctx context.Context, workspaceID, buildID, claimantID uuid.UUID, replacements []*sdkproto.ResourceReplacement) {
17+
}
1518
func (NoopReconciler) ReconcileAll(context.Context) error { return nil }
1619
func (NoopReconciler) SnapshotState(context.Context, database.Store) (*GlobalSnapshot, error) {
1720
return &GlobalSnapshot{}, nil

0 commit comments

Comments
 (0)