mirror of
https://github.com/cirruslabs/orchard.git
synced 2026-09-30 03:51:43 +02:00
API: introduce ability to watch a VM (#351)
* API: introduce ability to watch a VM
* Document ?watch=true for GET /vms/{name} in the OpenAPI specification
* WatchVM: ensure that goroutine is terminated on early return with error
* WatchVM: close channels on goroutine exit
* WatchVM: ensure that we wait for the goroutine after additional barriers
* WatchVM: ignore unexpected keys instead of throwing an error
* WatchVM: perform context-aware writes to a bounded channel
* WatchVM: don't forget to close errCh on goroutine exit too
* WatchVM: don't close readyCh in goroutine to avoid ambiguity
* WatchVM: filter out spurious KVs that signify VM deletion
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cirruslabs/orchard/internal/controller/lifecycle"
|
||||
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
|
||||
"github.com/cirruslabs/orchard/internal/responder"
|
||||
@@ -10,8 +14,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/samber/lo"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (controller *Controller) createVM(ctx *gin.Context) responder.Responder {
|
||||
@@ -150,6 +152,42 @@ func (controller *Controller) getVM(ctx *gin.Context) responder.Responder {
|
||||
|
||||
name := ctx.Param("name")
|
||||
|
||||
if ctx.Query("watch") == "true" {
|
||||
ctx.Header("Content-Type", "application/x-ndjson")
|
||||
|
||||
watchCh, errCh, err := controller.store.WatchVM(ctx, name)
|
||||
if err != nil {
|
||||
return responder.Error(err)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case watchMessage := <-watchCh:
|
||||
jsonBytes, err := json.Marshal(watchMessage)
|
||||
if err != nil {
|
||||
controller.logger.Errorf("failed to marshal watch message "+
|
||||
"for VM %q to JSON: %v", name, err)
|
||||
|
||||
return responder.Empty()
|
||||
}
|
||||
|
||||
if _, err = ctx.Writer.Write(jsonBytes); err != nil {
|
||||
return responder.Empty()
|
||||
}
|
||||
if _, err := ctx.Writer.WriteString("\n"); err != nil {
|
||||
return responder.Empty()
|
||||
}
|
||||
ctx.Writer.Flush()
|
||||
case err := <-errCh:
|
||||
controller.logger.Errorf("failed to watch VM %q in the DB: %v", name, err)
|
||||
|
||||
return responder.Empty()
|
||||
case <-ctx.Done():
|
||||
return responder.Empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return controller.storeView(func(txn storepkg.Transaction) responder.Responder {
|
||||
vm, err := txn.GetVM(name)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user