Skip to content

Commit f9a6adc

Browse files
authored
feat: claim prebuilds based on workspace parameters instead of preset id (#19279)
Closes #18356. This change finds and selects a matching preset if one was not chosen during workspace creation. This solidifies the relationship between presets and parameters. When a workspace is created without in explicitly chosen preset, it will now still be eligible to claim a prebuilt workspace if one is available.
1 parent 5e84d25 commit f9a6adc

File tree

15 files changed

+736
-37
lines changed

15 files changed

+736
-37
lines changed

coderd/database/dbauthz/dbauthz.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,14 @@ func (q *querier) FetchVolumesResourceMonitorsUpdatedAfter(ctx context.Context,
18371837
return q.db.FetchVolumesResourceMonitorsUpdatedAfter(ctx, updatedAt)
18381838
}
18391839

1840+
func (q *querier) FindMatchingPresetID(ctx context.Context, arg database.FindMatchingPresetIDParams) (uuid.UUID, error) {
1841+
_, err := q.GetTemplateVersionByID(ctx, arg.TemplateVersionID)
1842+
if err != nil {
1843+
return uuid.Nil, err
1844+
}
1845+
return q.db.FindMatchingPresetID(ctx, arg)
1846+
}
1847+
18401848
func (q *querier) GetAPIKeyByID(ctx context.Context, id string) (database.APIKey, error) {
18411849
return fetch(q.log, q.auth, q.db.GetAPIKeyByID)(ctx, id)
18421850
}

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4965,6 +4965,22 @@ func (s *MethodTestSuite) TestPrebuilds() {
49654965
template, policy.ActionUse,
49664966
).Errors(sql.ErrNoRows)
49674967
}))
4968+
s.Run("FindMatchingPresetID", s.Mocked(func(dbm *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
4969+
t1 := testutil.Fake(s.T(), faker, database.Template{})
4970+
tv := testutil.Fake(s.T(), faker, database.TemplateVersion{TemplateID: uuid.NullUUID{UUID: t1.ID, Valid: true}})
4971+
dbm.EXPECT().FindMatchingPresetID(gomock.Any(), database.FindMatchingPresetIDParams{
4972+
TemplateVersionID: tv.ID,
4973+
ParameterNames: []string{"test"},
4974+
ParameterValues: []string{"test"},
4975+
}).Return(uuid.Nil, nil).AnyTimes()
4976+
dbm.EXPECT().GetTemplateVersionByID(gomock.Any(), tv.ID).Return(tv, nil).AnyTimes()
4977+
dbm.EXPECT().GetTemplateByID(gomock.Any(), t1.ID).Return(t1, nil).AnyTimes()
4978+
check.Args(database.FindMatchingPresetIDParams{
4979+
TemplateVersionID: tv.ID,
4980+
ParameterNames: []string{"test"},
4981+
ParameterValues: []string{"test"},
4982+
}).Asserts(tv.RBACObject(t1), policy.ActionRead).Returns(uuid.Nil)
4983+
}))
49684984
s.Run("GetPrebuildMetrics", s.Subtest(func(_ database.Store, check *expects) {
49694985
check.Args().
49704986
Asserts(rbac.ResourceWorkspace.All(), policy.ActionRead)

coderd/database/dbmetrics/querymetrics.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/prebuilds.sql

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,30 @@ INNER JOIN organizations o ON o.id = w.organization_id
245245
WHERE NOT t.deleted AND wpb.build_number = 1
246246
GROUP BY t.name, tvp.name, o.name
247247
ORDER BY t.name, tvp.name, o.name;
248+
249+
-- name: FindMatchingPresetID :one
250+
-- FindMatchingPresetID finds a preset ID that is the largest exact subset of the provided parameters.
251+
-- It returns the preset ID if a match is found, or NULL if no match is found.
252+
-- The query finds presets where all preset parameters are present in the provided parameters,
253+
-- and returns the preset with the most parameters (largest subset).
254+
WITH provided_params AS (
255+
SELECT
256+
unnest(@parameter_names::text[]) AS name,
257+
unnest(@parameter_values::text[]) AS value
258+
),
259+
preset_matches AS (
260+
SELECT
261+
tvp.id AS template_version_preset_id,
262+
COALESCE(COUNT(tvpp.name), 0) AS total_preset_params,
263+
COALESCE(COUNT(pp.name), 0) AS matching_params
264+
FROM template_version_presets tvp
265+
LEFT JOIN template_version_preset_parameters tvpp ON tvpp.template_version_preset_id = tvp.id
266+
LEFT JOIN provided_params pp ON pp.name = tvpp.name AND pp.value = tvpp.value
267+
WHERE tvp.template_version_id = @template_version_id
268+
GROUP BY tvp.id
269+
)
270+
SELECT pm.template_version_preset_id
271+
FROM preset_matches pm
272+
WHERE pm.total_preset_params = pm.matching_params -- All preset parameters must match
273+
ORDER BY pm.total_preset_params DESC -- Return the preset with the most parameters
274+
LIMIT 1;

coderd/prebuilds/parameters.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package prebuilds
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"errors"
7+
8+
"github.com/google/uuid"
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/coder/v2/coderd/database"
12+
)
13+
14+
// FindMatchingPresetID finds a preset ID that matches the provided parameters.
15+
// It returns the preset ID if a match is found, or uuid.Nil if no match is found.
16+
// The function performs a bidirectional comparison to ensure all parameters match exactly.
17+
func FindMatchingPresetID(
18+
ctx context.Context,
19+
store database.Store,
20+
templateVersionID uuid.UUID,
21+
parameterNames []string,
22+
parameterValues []string,
23+
) (uuid.UUID, error) {
24+
if len(parameterNames) != len(parameterValues) {
25+
return uuid.Nil, xerrors.New("parameter names and values must have the same length")
26+
}
27+
28+
result, err := store.FindMatchingPresetID(ctx, database.FindMatchingPresetIDParams{
29+
TemplateVersionID: templateVersionID,
30+
ParameterNames: parameterNames,
31+
ParameterValues: parameterValues,
32+
})
33+
if err != nil {
34+
// Handle the case where no matching preset is found (no rows returned)
35+
if errors.Is(err, sql.ErrNoRows) {
36+
return uuid.Nil, nil
37+
}
38+
return uuid.Nil, xerrors.Errorf("find matching preset ID: %w", err)
39+
}
40+
41+
return result, nil
42+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy