Skip to content

Commit f18c7c0

Browse files
committed
chore: Add watch workspace endpoint
1 parent b55d83c commit f18c7c0

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ func New(options *Options) (http.Handler, func()) {
316316
r.Route("/autostop", func(r chi.Router) {
317317
r.Put("/", api.putWorkspaceAutostop)
318318
})
319+
r.HandleFunc("/watch", api.watchWorkspace)
319320
})
320321
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {
321322
r.Use(

coderd/workspaces.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ import (
77
"errors"
88
"fmt"
99
"net/http"
10+
"time"
1011

12+
"cdr.dev/slog"
1113
"github.com/go-chi/chi/v5"
1214
"github.com/google/uuid"
1315
"github.com/moby/moby/pkg/namesgenerator"
1416
"golang.org/x/sync/errgroup"
1517
"golang.org/x/xerrors"
18+
"nhooyr.io/websocket"
19+
"nhooyr.io/websocket/wsjson"
1620

1721
"github.com/coder/coder/coderd/autobuild/schedule"
1822
"github.com/coder/coder/coderd/database"
@@ -497,6 +501,94 @@ func (api *api) putWorkspaceAutostop(rw http.ResponseWriter, r *http.Request) {
497501
}
498502
}
499503

504+
func (api *api) watchWorkspace(rw http.ResponseWriter, r *http.Request) {
505+
workspace := httpmw.WorkspaceParam(r)
506+
507+
c, err := websocket.Accept(rw, r, &websocket.AcceptOptions{
508+
// Fix for Safari 15.1:
509+
// There is a bug in latest Safari in which compressed web socket traffic
510+
// isn't handled correctly. Turning off compression is a workaround:
511+
// https://github.com/nhooyr/websocket/issues/218
512+
CompressionMode: websocket.CompressionDisabled,
513+
})
514+
if err != nil {
515+
api.Logger.Warn(r.Context(), "accept websocket connection", slog.Error(err))
516+
return
517+
}
518+
defer c.Close(websocket.StatusInternalError, "internal error")
519+
520+
ctx := c.CloseRead(r.Context())
521+
522+
// Send a heartbeat every 15 seconds to avoid the websocket being killed.
523+
go func() {
524+
ticker := time.NewTicker(time.Second * 15)
525+
defer ticker.Stop()
526+
527+
for {
528+
select {
529+
case <-ctx.Done():
530+
return
531+
case <-ticker.C:
532+
err := c.Ping(ctx)
533+
if err != nil {
534+
return
535+
}
536+
}
537+
}
538+
}()
539+
540+
t := time.NewTicker(time.Second * 1)
541+
defer t.Stop()
542+
for {
543+
select {
544+
case <-t.C:
545+
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspace.ID)
546+
if err != nil {
547+
_ = wsjson.Write(ctx, c, httpapi.Response{
548+
Message: fmt.Sprintf("get workspace: %s", err),
549+
})
550+
return
551+
}
552+
build, err := api.Database.GetWorkspaceBuildByWorkspaceIDWithoutAfter(r.Context(), workspace.ID)
553+
if err != nil {
554+
_ = wsjson.Write(ctx, c, httpapi.Response{
555+
Message: fmt.Sprintf("get workspace build: %s", err),
556+
})
557+
return
558+
}
559+
var (
560+
group errgroup.Group
561+
job database.ProvisionerJob
562+
template database.Template
563+
owner database.User
564+
)
565+
group.Go(func() (err error) {
566+
job, err = api.Database.GetProvisionerJobByID(r.Context(), build.JobID)
567+
return err
568+
})
569+
group.Go(func() (err error) {
570+
template, err = api.Database.GetTemplateByID(r.Context(), workspace.TemplateID)
571+
return err
572+
})
573+
group.Go(func() (err error) {
574+
owner, err = api.Database.GetUserByID(r.Context(), workspace.OwnerID)
575+
return err
576+
})
577+
err = group.Wait()
578+
if err != nil {
579+
_ = wsjson.Write(ctx, c, httpapi.Response{
580+
Message: fmt.Sprintf("fetch resource: %s", err),
581+
})
582+
return
583+
}
584+
585+
_ = wsjson.Write(ctx, c, convertWorkspace(workspace, convertWorkspaceBuild(build, convertProvisionerJob(job)), template, owner))
586+
case <-ctx.Done():
587+
return
588+
}
589+
}
590+
}
591+
500592
func convertWorkspaces(ctx context.Context, db database.Store, workspaces []database.Workspace) ([]codersdk.Workspace, error) {
501593
workspaceIDs := make([]uuid.UUID, 0, len(workspaces))
502594
templateIDs := make([]uuid.UUID, 0, len(workspaces))

0 commit comments

Comments
 (0)