Skip to content

Commit 0760f8d

Browse files
committed
chore: Add watch workspace endpoint
1 parent 9f402fa commit 0760f8d

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
@@ -311,6 +311,7 @@ func New(options *Options) (http.Handler, func()) {
311311
r.Put("/", api.putWorkspaceAutostop)
312312
})
313313
})
314+
r.HandleFunc("/watch", api.watchWorkspace)
314315
})
315316
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {
316317
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"
@@ -535,6 +539,94 @@ func (api *api) putWorkspaceAutostop(rw http.ResponseWriter, r *http.Request) {
535539
}
536540
}
537541

542+
func (api *api) watchWorkspace(rw http.ResponseWriter, r *http.Request) {
543+
workspace := httpmw.WorkspaceParam(r)
544+
545+
c, err := websocket.Accept(rw, r, &websocket.AcceptOptions{
546+
// Fix for Safari 15.1:
547+
// There is a bug in latest Safari in which compressed web socket traffic
548+
// isn't handled correctly. Turning off compression is a workaround:
549+
// https://github.com/nhooyr/websocket/issues/218
550+
CompressionMode: websocket.CompressionDisabled,
551+
})
552+
if err != nil {
553+
api.Logger.Warn(r.Context(), "accept websocket connection", slog.Error(err))
554+
return
555+
}
556+
defer c.Close(websocket.StatusInternalError, "internal error")
557+
558+
ctx := c.CloseRead(r.Context())
559+
560+
// Send a heartbeat every 15 seconds to avoid the websocket being killed.
561+
go func() {
562+
ticker := time.NewTicker(time.Second * 15)
563+
defer ticker.Stop()
564+
565+
for {
566+
select {
567+
case <-ctx.Done():
568+
return
569+
case <-ticker.C:
570+
err := c.Ping(ctx)
571+
if err != nil {
572+
return
573+
}
574+
}
575+
}
576+
}()
577+
578+
t := time.NewTicker(time.Second * 1)
579+
defer t.Stop()
580+
for {
581+
select {
582+
case <-t.C:
583+
workspace, err := api.Database.GetWorkspaceByID(r.Context(), workspace.ID)
584+
if err != nil {
585+
_ = wsjson.Write(ctx, c, httpapi.Response{
586+
Message: fmt.Sprintf("get workspace: %s", err),
587+
})
588+
return
589+
}
590+
build, err := api.Database.GetWorkspaceBuildByWorkspaceIDWithoutAfter(r.Context(), workspace.ID)
591+
if err != nil {
592+
_ = wsjson.Write(ctx, c, httpapi.Response{
593+
Message: fmt.Sprintf("get workspace build: %s", err),
594+
})
595+
return
596+
}
597+
var (
598+
group errgroup.Group
599+
job database.ProvisionerJob
600+
template database.Template
601+
owner database.User
602+
)
603+
group.Go(func() (err error) {
604+
job, err = api.Database.GetProvisionerJobByID(r.Context(), build.JobID)
605+
return err
606+
})
607+
group.Go(func() (err error) {
608+
template, err = api.Database.GetTemplateByID(r.Context(), workspace.TemplateID)
609+
return err
610+
})
611+
group.Go(func() (err error) {
612+
owner, err = api.Database.GetUserByID(r.Context(), workspace.OwnerID)
613+
return err
614+
})
615+
err = group.Wait()
616+
if err != nil {
617+
_ = wsjson.Write(ctx, c, httpapi.Response{
618+
Message: fmt.Sprintf("fetch resource: %s", err),
619+
})
620+
return
621+
}
622+
623+
_ = wsjson.Write(ctx, c, convertWorkspace(workspace, convertWorkspaceBuild(build, convertProvisionerJob(job)), template, owner))
624+
case <-ctx.Done():
625+
return
626+
}
627+
}
628+
}
629+
538630
func convertWorkspaces(ctx context.Context, db database.Store, workspaces []database.Workspace) ([]codersdk.Workspace, error) {
539631
workspaceIDs := make([]uuid.UUID, 0, len(workspaces))
540632
templateIDs := make([]uuid.UUID, 0, len(workspaces))

0 commit comments

Comments
 (0)