Skip to content

Commit 4f193d7

Browse files
committed
:)
1 parent 5414018 commit 4f193d7

File tree

3 files changed

+105
-61
lines changed

3 files changed

+105
-61
lines changed

coderd/database/dbauthz/dbauthz.go

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,13 +1852,6 @@ func (q *querier) GetLatestCryptoKeyByFeature(ctx context.Context, feature datab
18521852
return q.db.GetLatestCryptoKeyByFeature(ctx, feature)
18531853
}
18541854

1855-
func (q *querier) GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
1856-
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
1857-
return nil, err
1858-
}
1859-
return q.db.GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx, ids)
1860-
}
1861-
18621855
func (q *querier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
18631856
if _, err := q.GetWorkspaceByID(ctx, workspaceID); err != nil {
18641857
return database.WorkspaceBuild{}, err
@@ -2466,23 +2459,12 @@ func (q *querier) GetTemplateVersionParameters(ctx context.Context, templateVers
24662459
}
24672460

24682461
func (q *querier) GetTemplateVersionTerraformValues(ctx context.Context, templateVersionID uuid.UUID) (database.TemplateVersionTerraformValue, error) {
2469-
tv, err := q.db.GetTemplateVersionByID(ctx, templateVersionID)
2470-
if err != nil {
2471-
return database.TemplateVersionTerraformValue{}, err
2472-
}
2473-
2474-
var object rbac.Objecter
2475-
template, err := q.db.GetTemplateByID(ctx, tv.TemplateID.UUID)
2462+
// The template_version_terraform_values table should follow the same access
2463+
// control as the template_version table. Rather than reimplement the checks,
2464+
// we just defer to existing implementation. (plus we'd need to use this query
2465+
// to reimplement the proper checks anyway)
2466+
_, err := q.GetTemplateVersionByID(ctx, templateVersionID)
24762467
if err != nil {
2477-
if !errors.Is(err, sql.ErrNoRows) {
2478-
return database.TemplateVersionTerraformValue{}, err
2479-
}
2480-
object = rbac.ResourceTemplate.InOrg(tv.OrganizationID)
2481-
} else {
2482-
object = tv.RBACObject(template)
2483-
}
2484-
2485-
if err := q.authorizeContext(ctx, policy.ActionRead, object); err != nil {
24862468
return database.TemplateVersionTerraformValue{}, err
24872469
}
24882470
return q.db.GetTemplateVersionTerraformValues(ctx, templateVersionID)
@@ -2896,13 +2878,6 @@ func (q *querier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg datab
28962878
return q.db.GetWorkspaceAppByAgentIDAndSlug(ctx, arg)
28972879
}
28982880

2899-
func (q *querier) GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
2900-
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
2901-
return nil, err
2902-
}
2903-
return q.db.GetWorkspaceAppStatusesByAppIDs(ctx, ids)
2904-
}
2905-
29062881
func (q *querier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]database.WorkspaceApp, error) {
29072882
if _, err := q.GetWorkspaceByAgentID(ctx, agentID); err != nil {
29082883
return nil, err
@@ -3596,13 +3571,6 @@ func (q *querier) InsertWorkspaceAppStats(ctx context.Context, arg database.Inse
35963571
return q.db.InsertWorkspaceAppStats(ctx, arg)
35973572
}
35983573

3599-
func (q *querier) InsertWorkspaceAppStatus(ctx context.Context, arg database.InsertWorkspaceAppStatusParams) (database.WorkspaceAppStatus, error) {
3600-
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
3601-
return database.WorkspaceAppStatus{}, err
3602-
}
3603-
return q.db.InsertWorkspaceAppStatus(ctx, arg)
3604-
}
3605-
36063574
func (q *querier) InsertWorkspaceBuild(ctx context.Context, arg database.InsertWorkspaceBuildParams) error {
36073575
w, err := q.db.GetWorkspaceByID(ctx, arg.WorkspaceID)
36083576
if err != nil {

coderd/templateversions.go

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/json"
99
"errors"
1010
"fmt"
11+
"math"
1112
"net/http"
1213
"os"
1314

@@ -35,6 +36,7 @@ import (
3536
"github.com/coder/coder/v2/coderd/tracing"
3637
"github.com/coder/coder/v2/coderd/util/ptr"
3738
"github.com/coder/coder/v2/codersdk"
39+
"github.com/coder/coder/v2/codersdk/wsjson"
3840
"github.com/coder/coder/v2/examples"
3941
"github.com/coder/coder/v2/provisioner/terraform/tfparse"
4042
"github.com/coder/coder/v2/provisionersdk"
@@ -43,7 +45,6 @@ import (
4345
previewtypes "github.com/coder/preview/types"
4446
previewweb "github.com/coder/preview/web"
4547
"github.com/coder/websocket"
46-
"github.com/coder/websocket/wsjson"
4748
)
4849

4950
// @Summary Get template version by ID
@@ -299,7 +300,23 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
299300
return
300301
}
301302

302-
fileID, err := api.Database.GetFileIDByTemplateVersionID(ctx, templateVersion.ID)
303+
// Having the Terraform plan available for the evaluation engine is helpful
304+
// for populating values from data blocks, but isn't strictly required. If
305+
// we don't have a cached plan available, we just use an empty one instead.
306+
var plan json.RawMessage = []byte("{}")
307+
tf, err := api.Database.GetTemplateVersionTerraformValues(ctx, templateVersion.ID)
308+
if err == nil {
309+
plan = tf.CachedPlan
310+
}
311+
312+
input := preview.Input{
313+
PlanJSON: plan,
314+
ParameterValues: map[string]string{},
315+
Owner: previewtypes.WorkspaceOwner{},
316+
}
317+
318+
fileCtx := dbauthz.AsProvisionerd(ctx)
319+
fileID, err := api.Database.GetFileIDByTemplateVersionID(fileCtx, templateVersion.ID)
303320
if err != nil {
304321
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
305322
Message: "Internal error finding template version Terraform.",
@@ -308,7 +325,8 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
308325
return
309326
}
310327

311-
fs, err := api.FileCache.Acquire(fileID)
328+
fs, err := api.FileCache.Acquire(fileCtx, fileID)
329+
defer api.FileCache.Release(fileID)
312330
if err != nil {
313331
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
314332
Message: "Internal error fetching template version Terraform.",
@@ -317,37 +335,45 @@ func (api *API) templateVersionDynamicParameters(rw http.ResponseWriter, r *http
317335
return
318336
}
319337

320-
// Having the Terraform plan available for the evaluation engine is helpful
321-
// for populating values from data blocks, but isn't strictly required. If
322-
// we don't have a cached plan available, we just use an empty one instead.
323-
var plan json.RawMessage = []byte("{}")
324-
tf, err := api.Database.GetTemplateVersionTerraformValues(ctx, templateVersion.ID)
325-
if err == nil {
326-
plan = tf.CachedPlan
327-
}
328-
329-
// TODO: idk if I need to set any options here. it doesn't seem like anyone
330-
// else does, but steven had to set a * domain in the prototype. do we need
331-
// to specify some host or anything?
332-
conn, err := websocket.Accept(rw, r, &websocket.AcceptOptions{})
338+
conn, err := websocket.Accept(rw, r, nil)
333339
if err != nil {
334-
httpapi.Write(ctx, rw, http.StatusBadGateway, codersdk.Response{
335-
Message: "devour feculence mr drummond",
340+
httpapi.Write(ctx, rw, http.StatusUpgradeRequired, codersdk.Response{
341+
Message: "Failed to accept WebSocket.",
342+
Detail: err.Error(),
336343
})
337344
return
338345
}
339346

340-
input := preview.Input{
341-
PlanJSON: plan,
342-
ParameterValues: map[string]string{},
343-
Owner: previewtypes.WorkspaceOwner{},
344-
}
347+
stream := wsjson.NewStream[previewweb.Request, previewweb.Response](conn, websocket.MessageText, websocket.MessageText, api.Logger)
345348

349+
// Send an initial form state, computed without any user input.
346350
result, diagnostics := preview.Preview(ctx, input, fs)
347-
wsjson.Write(ctx, conn, previewweb.Response{
351+
stream.Send(previewweb.Response{
352+
// or maybe it could be -1 or something? it just has to be unique from
353+
// anything a client could reasonably send.
354+
ID: math.MaxInt32,
348355
Parameters: result.Parameters,
349356
Diagnostics: previewtypes.Diagnostics(diagnostics),
350357
})
358+
359+
// As the user types into the form, reprocess the state using their input,
360+
// and respond with updates.
361+
updates := stream.Chan()
362+
for {
363+
select {
364+
case <-ctx.Done():
365+
return
366+
case update := <-updates:
367+
newInput := input
368+
newInput.ParameterValues = update.Inputs
369+
result, diagnostics := preview.Preview(ctx, input, fs)
370+
stream.Send(previewweb.Response{
371+
ID: update.ID,
372+
Parameters: result.Parameters,
373+
Diagnostics: previewtypes.Diagnostics(diagnostics),
374+
})
375+
}
376+
}
351377
}
352378

353379
// @Summary Get rich parameters by template version

codersdk/wsjson/stream.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package wsjson
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"cdr.dev/slog"
8+
"github.com/coder/websocket"
9+
"golang.org/x/xerrors"
10+
)
11+
12+
// Stream is a two-way messaging interface over a WebSocket connection.
13+
// As an implementation detail, we cannot currently use Encoder to implement
14+
// the writing side of things because it only supports sending one message, and
15+
// then immediately closing the WebSocket.
16+
type Stream[R any, W any] struct {
17+
conn *websocket.Conn
18+
r *Decoder[R]
19+
20+
writeType websocket.MessageType
21+
}
22+
23+
func NewStream[R any, W any](conn *websocket.Conn, readType, writeType websocket.MessageType, logger slog.Logger) *Stream[R, W] {
24+
return &Stream[R, W]{
25+
conn: conn,
26+
r: NewDecoder[R](conn, readType, logger),
27+
writeType: writeType,
28+
}
29+
}
30+
31+
func (s *Stream[R, W]) Chan() <-chan R {
32+
return s.r.Chan()
33+
}
34+
35+
func (s *Stream[R, W]) Send(v W) error {
36+
w, err := s.conn.Writer(context.Background(), s.writeType)
37+
if err != nil {
38+
return xerrors.Errorf("get websocket writer: %w", err)
39+
}
40+
j := json.NewEncoder(w)
41+
err = j.Encode(v)
42+
if err != nil {
43+
return xerrors.Errorf("encode json: %w", err)
44+
}
45+
return nil
46+
}
47+
48+
func (s *Stream[R, W]) Close(c websocket.StatusCode) error {
49+
return s.conn.Close(c, "")
50+
}

0 commit comments

Comments
 (0)