@@ -34,7 +34,7 @@ import (
34
34
// This is useful when a proxy is created or deleted. Errors will be logged.
35
35
func (api * API ) forceWorkspaceProxyHealthUpdate (ctx context.Context ) {
36
36
if err := api .ProxyHealth .ForceUpdate (ctx ); err != nil {
37
- api .Logger .Error (ctx , "force proxy health update" , slog .Error (err ))
37
+ api .Logger .Warn (ctx , "force proxy health update" , slog .Error (err ))
38
38
}
39
39
}
40
40
@@ -316,17 +316,17 @@ func (api *API) workspaceProxyIssueSignedAppToken(rw http.ResponseWriter, r *htt
316
316
// in the database and returns a signed token that can be used to authenticate
317
317
// tokens.
318
318
//
319
- // This is called periodically by the proxy in the background (once per minute
320
- // per replica) to ensure that the proxy is still registered and the
321
- // corresponding replica table entry is refreshed.
319
+ // This is called periodically by the proxy in the background (every 30s per
320
+ // replica) to ensure that the proxy is still registered and the corresponding
321
+ // replica table entry is refreshed.
322
322
//
323
323
// @Summary Register workspace proxy
324
324
// @ID register-workspace-proxy
325
325
// @Security CoderSessionToken
326
326
// @Accept json
327
327
// @Produce json
328
328
// @Tags Enterprise
329
- // @Param request body wsproxysdk.RegisterWorkspaceProxyRequest true "Issue signed app token request"
329
+ // @Param request body wsproxysdk.RegisterWorkspaceProxyRequest true "Register workspace proxy request"
330
330
// @Success 201 {object} wsproxysdk.RegisterWorkspaceProxyResponse
331
331
// @Router /workspaceproxies/me/register [post]
332
332
// @x-apidocgen {"skip": true}
@@ -367,6 +367,13 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request)
367
367
}
368
368
}
369
369
370
+ if req .ReplicaID == uuid .Nil {
371
+ httpapi .Write (ctx , rw , http .StatusBadRequest , codersdk.Response {
372
+ Message : "Replica ID is invalid." ,
373
+ })
374
+ return
375
+ }
376
+
370
377
// TODO: get region ID
371
378
var regionID int32 = 1234
372
379
@@ -472,6 +479,78 @@ func (api *API) workspaceProxyRegister(rw http.ResponseWriter, r *http.Request)
472
479
go api .forceWorkspaceProxyHealthUpdate (api .ctx )
473
480
}
474
481
482
+ // @Summary Deregister workspace proxy
483
+ // @ID deregister-workspace-proxy
484
+ // @Security CoderSessionToken
485
+ // @Accept json
486
+ // @Tags Enterprise
487
+ // @Param request body wsproxysdk.DeregisterWorkspaceProxyRequest true "Deregister workspace proxy request"
488
+ // @Success 204
489
+ // @Router /workspaceproxies/me/deregister [post]
490
+ // @x-apidocgen {"skip": true}
491
+ func (api * API ) workspaceProxyDeregister (rw http.ResponseWriter , r * http.Request ) {
492
+ ctx := r .Context ()
493
+
494
+ var req wsproxysdk.DeregisterWorkspaceProxyRequest
495
+ if ! httpapi .Read (ctx , rw , r , & req ) {
496
+ return
497
+ }
498
+
499
+ err := api .Database .InTx (func (db database.Store ) error {
500
+ now := time .Now ()
501
+ replica , err := db .GetReplicaByID (ctx , req .ReplicaID )
502
+ if err != nil {
503
+ return xerrors .Errorf ("get replica: %w" , err )
504
+ }
505
+
506
+ if replica .StoppedAt .Valid && ! replica .StartedAt .IsZero () {
507
+ // TODO: sadly this results in 500 when it should be 400
508
+ return xerrors .Errorf ("replica %s is already marked stopped" , replica .ID )
509
+ }
510
+
511
+ replica , err = db .UpdateReplica (ctx , database.UpdateReplicaParams {
512
+ ID : replica .ID ,
513
+ UpdatedAt : now ,
514
+ StartedAt : replica .StartedAt ,
515
+ StoppedAt : sql.NullTime {
516
+ Valid : true ,
517
+ Time : now ,
518
+ },
519
+ RelayAddress : replica .RelayAddress ,
520
+ RegionID : replica .RegionID ,
521
+ Hostname : replica .Hostname ,
522
+ Version : replica .Version ,
523
+ Error : replica .Error ,
524
+ DatabaseLatency : replica .DatabaseLatency ,
525
+ Primary : replica .Primary ,
526
+ })
527
+ if err != nil {
528
+ return xerrors .Errorf ("update replica: %w" , err )
529
+ }
530
+
531
+ return nil
532
+ }, nil )
533
+ if httpapi .Is404Error (err ) {
534
+ httpapi .ResourceNotFound (rw )
535
+ return
536
+ }
537
+ if err != nil {
538
+ httpapi .InternalServerError (rw , err )
539
+ return
540
+ }
541
+
542
+ // Publish a replicasync event with a nil ID so every replica (yes, even the
543
+ // current replica) will refresh its replicas list.
544
+ err = api .Pubsub .Publish (replicasync .PubsubEvent , []byte (uuid .Nil .String ()))
545
+ if err != nil {
546
+ httpapi .InternalServerError (rw , err )
547
+ return
548
+ }
549
+
550
+ rw .WriteHeader (http .StatusNoContent )
551
+ go api .forceWorkspaceProxyHealthUpdate (api .ctx )
552
+ }
553
+
475
554
// reconnectingPTYSignedToken issues a signed app token for use when connecting
476
555
// to the reconnecting PTY websocket on an external workspace proxy. This is set
477
556
// by the client as a query parameter when connecting.
0 commit comments