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

Skip to content

Commit e23873f

Browse files
authored
feat: add endpoint for resolving autostart status (coder#10507)
1 parent cf8ee78 commit e23873f

File tree

11 files changed

+425
-0
lines changed

11 files changed

+425
-0
lines changed

coderd/apidoc/docs.go

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

coderd/apidoc/swagger.json

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

coderd/coderd.go

+1
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,7 @@ func New(options *Options) *API {
885885
r.Put("/extend", api.putExtendWorkspace)
886886
r.Put("/dormant", api.putWorkspaceDormant)
887887
r.Put("/autoupdates", api.putWorkspaceAutoupdates)
888+
r.Get("/resolve-autostart", api.resolveAutostart)
888889
})
889890
})
890891
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {

coderd/database/db2sdk/db2sdk.go

+14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/google/uuid"
99
"golang.org/x/exp/slices"
10+
"golang.org/x/xerrors"
1011

1112
"github.com/coder/coder/v2/coderd/database"
1213
"github.com/coder/coder/v2/coderd/parameter"
@@ -30,6 +31,19 @@ func WorkspaceBuildParameter(p database.WorkspaceBuildParameter) codersdk.Worksp
3031
}
3132
}
3233

34+
func TemplateVersionParameters(params []database.TemplateVersionParameter) ([]codersdk.TemplateVersionParameter, error) {
35+
out := make([]codersdk.TemplateVersionParameter, len(params))
36+
var err error
37+
for i, p := range params {
38+
out[i], err = TemplateVersionParameter(p)
39+
if err != nil {
40+
return nil, xerrors.Errorf("convert template version parameter %q: %w", p.Name, err)
41+
}
42+
}
43+
44+
return out, nil
45+
}
46+
3347
func TemplateVersionParameter(param database.TemplateVersionParameter) (codersdk.TemplateVersionParameter, error) {
3448
options, err := templateVersionParameterOptions(param.Options)
3549
if err != nil {

coderd/workspaces.go

+95
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"cdr.dev/slog"
1818
"github.com/coder/coder/v2/coderd/audit"
1919
"github.com/coder/coder/v2/coderd/database"
20+
"github.com/coder/coder/v2/coderd/database/db2sdk"
2021
"github.com/coder/coder/v2/coderd/database/dbauthz"
2122
"github.com/coder/coder/v2/coderd/database/dbtime"
2223
"github.com/coder/coder/v2/coderd/database/provisionerjobs"
@@ -1059,6 +1060,100 @@ func (api *API) putWorkspaceAutoupdates(rw http.ResponseWriter, r *http.Request)
10591060
rw.WriteHeader(http.StatusNoContent)
10601061
}
10611062

1063+
// @Summary Resolve workspace autostart by id.
1064+
// @ID resolve-workspace-autostart-by-id
1065+
// @Security CoderSessionToken
1066+
// @Produce json
1067+
// @Tags Workspaces
1068+
// @Param workspace path string true "Workspace ID" format(uuid)
1069+
// @Success 200 {object} codersdk.ResolveAutostartResponse
1070+
// @Router /workspaces/{workspace}/resolve-autostart [get]
1071+
func (api *API) resolveAutostart(rw http.ResponseWriter, r *http.Request) {
1072+
var (
1073+
ctx = r.Context()
1074+
workspace = httpmw.WorkspaceParam(r)
1075+
)
1076+
1077+
template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
1078+
if err != nil {
1079+
httpapi.InternalServerError(rw, err)
1080+
return
1081+
}
1082+
1083+
templateAccessControl := (*(api.AccessControlStore.Load())).GetTemplateAccessControl(template)
1084+
useActiveVersion := templateAccessControl.RequireActiveVersion || workspace.AutomaticUpdates == database.AutomaticUpdatesAlways
1085+
if !useActiveVersion {
1086+
httpapi.Write(ctx, rw, http.StatusOK, codersdk.ResolveAutostartResponse{})
1087+
return
1088+
}
1089+
1090+
build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
1091+
if err != nil {
1092+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1093+
Message: "Internal error fetching latest workspace build.",
1094+
Detail: err.Error(),
1095+
})
1096+
return
1097+
}
1098+
1099+
if build.TemplateVersionID == template.ActiveVersionID {
1100+
httpapi.Write(ctx, rw, http.StatusOK, codersdk.ResolveAutostartResponse{})
1101+
return
1102+
}
1103+
1104+
version, err := api.Database.GetTemplateVersionByID(ctx, template.ActiveVersionID)
1105+
if err != nil {
1106+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1107+
Message: "Internal error fetching template version.",
1108+
Detail: err.Error(),
1109+
})
1110+
return
1111+
}
1112+
1113+
dbVersionParams, err := api.Database.GetTemplateVersionParameters(ctx, version.ID)
1114+
if err != nil {
1115+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1116+
Message: "Internal error fetching template version parameters.",
1117+
Detail: err.Error(),
1118+
})
1119+
return
1120+
}
1121+
1122+
dbBuildParams, err := api.Database.GetWorkspaceBuildParameters(ctx, build.ID)
1123+
if err != nil {
1124+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1125+
Message: "Internal error fetching latest workspace build parameters.",
1126+
Detail: err.Error(),
1127+
})
1128+
return
1129+
}
1130+
1131+
versionParams, err := db2sdk.TemplateVersionParameters(dbVersionParams)
1132+
if err != nil {
1133+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1134+
Message: "Internal error converting template version parameters.",
1135+
Detail: err.Error(),
1136+
})
1137+
return
1138+
}
1139+
1140+
resolver := codersdk.ParameterResolver{
1141+
Rich: db2sdk.WorkspaceBuildParameters(dbBuildParams),
1142+
}
1143+
1144+
var response codersdk.ResolveAutostartResponse
1145+
for _, param := range versionParams {
1146+
_, err := resolver.ValidateResolve(param, nil)
1147+
// There's a parameter mismatch if we get an error back from the
1148+
// resolver.
1149+
response.ParameterMismatch = err != nil
1150+
if response.ParameterMismatch {
1151+
break
1152+
}
1153+
}
1154+
httpapi.Write(ctx, rw, http.StatusOK, response)
1155+
}
1156+
10621157
// @Summary Watch workspace by ID
10631158
// @ID watch-workspace-by-id
10641159
// @Security CoderSessionToken

coderd/workspaces_test.go

+93
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,99 @@ func TestWorkspace(t *testing.T) {
340340
})
341341
}
342342

343+
func TestResolveAutostart(t *testing.T) {
344+
t.Parallel()
345+
346+
t.Run("OK", func(t *testing.T) {
347+
t.Parallel()
348+
ownerClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
349+
owner := coderdtest.CreateFirstUser(t, ownerClient)
350+
version1 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, nil)
351+
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version1.ID)
352+
template := coderdtest.CreateTemplate(t, ownerClient, owner.OrganizationID, version1.ID)
353+
354+
params := &echo.Responses{
355+
Parse: echo.ParseComplete,
356+
ProvisionPlan: []*proto.Response{
357+
{
358+
Type: &proto.Response_Plan{
359+
Plan: &proto.PlanComplete{
360+
Parameters: []*proto.RichParameter{
361+
{
362+
Name: "param",
363+
Description: "param",
364+
Required: true,
365+
Mutable: true,
366+
},
367+
},
368+
},
369+
},
370+
},
371+
},
372+
ProvisionApply: echo.ApplyComplete,
373+
}
374+
version2 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, params, func(ctvr *codersdk.CreateTemplateVersionRequest) {
375+
ctvr.TemplateID = template.ID
376+
})
377+
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version2.ID)
378+
379+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
380+
defer cancel()
381+
382+
client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID)
383+
workspace := coderdtest.CreateWorkspace(t, client, owner.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
384+
cwr.AutomaticUpdates = codersdk.AutomaticUpdatesAlways
385+
})
386+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
387+
388+
err := ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{
389+
ID: version2.ID,
390+
})
391+
require.NoError(t, err)
392+
393+
// Autostart shouldn't be possible if parameters do not match.
394+
resp, err := client.ResolveAutostart(ctx, workspace.ID.String())
395+
require.NoError(t, err)
396+
require.True(t, resp.ParameterMismatch)
397+
398+
update, err := client.CreateWorkspaceBuild(ctx, workspace.ID, codersdk.CreateWorkspaceBuildRequest{
399+
TemplateVersionID: version2.ID,
400+
Transition: codersdk.WorkspaceTransitionStart,
401+
RichParameterValues: []codersdk.WorkspaceBuildParameter{
402+
{
403+
Name: "param",
404+
Value: "Hello",
405+
},
406+
},
407+
})
408+
require.NoError(t, err)
409+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, update.ID)
410+
411+
// We should be able to autostart since parameters are updated.
412+
resp, err = client.ResolveAutostart(ctx, workspace.ID.String())
413+
require.NoError(t, err)
414+
require.False(t, resp.ParameterMismatch)
415+
416+
// Create one last version where the parameters are the same as the previous
417+
// version.
418+
version3 := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, params, func(ctvr *codersdk.CreateTemplateVersionRequest) {
419+
ctvr.TemplateID = template.ID
420+
})
421+
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version3.ID)
422+
423+
err = ownerClient.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{
424+
ID: version3.ID,
425+
})
426+
require.NoError(t, err)
427+
428+
// Even though we're out of date we should still be able to autostart
429+
// since parameters resolve.
430+
resp, err = client.ResolveAutostart(ctx, workspace.ID.String())
431+
require.NoError(t, err)
432+
require.False(t, resp.ParameterMismatch)
433+
})
434+
}
435+
343436
func TestAdminViewAllWorkspaces(t *testing.T) {
344437
t.Parallel()
345438
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})

codersdk/workspaces.go

+17
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,23 @@ func (c *Client) WorkspaceQuota(ctx context.Context, userID string) (WorkspaceQu
449449
return quota, json.NewDecoder(res.Body).Decode(&quota)
450450
}
451451

452+
type ResolveAutostartResponse struct {
453+
ParameterMismatch bool `json:"parameter_mismatch"`
454+
}
455+
456+
func (c *Client) ResolveAutostart(ctx context.Context, workspaceID string) (ResolveAutostartResponse, error) {
457+
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaces/%s/resolve-autostart", workspaceID), nil)
458+
if err != nil {
459+
return ResolveAutostartResponse{}, err
460+
}
461+
defer res.Body.Close()
462+
if res.StatusCode != http.StatusOK {
463+
return ResolveAutostartResponse{}, ReadBodyAsError(res)
464+
}
465+
var response ResolveAutostartResponse
466+
return response, json.NewDecoder(res.Body).Decode(&response)
467+
}
468+
452469
// WorkspaceNotifyChannel is the PostgreSQL NOTIFY
453470
// channel to listen for updates on. The payload is empty,
454471
// because the size of a workspace payload can be very large.

docs/api/schemas.md

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

0 commit comments

Comments
 (0)