orchard/internal/controller/api_workers.go

167 lines
4.8 KiB
Go

package controller
import (
"errors"
"net/http"
"time"
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
"github.com/cirruslabs/orchard/internal/responder"
"github.com/cirruslabs/orchard/internal/simplename"
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
"github.com/gin-gonic/gin"
)
func (controller *Controller) createWorker(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleComputeWrite); responder != nil {
return responder
}
var worker v1.Worker
if err := ctx.ShouldBindJSON(&worker); err != nil {
return responder.JSON(http.StatusBadRequest, NewErrorResponse("invalid JSON was provided"))
}
if worker.Name == "" {
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("worker name is empty"))
} else if err := simplename.Validate(worker.Name); err != nil {
return responder.JSON(http.StatusPreconditionFailed,
NewErrorResponse("worker name %v", err))
}
// Provide platform defaults
if worker.Arch == "" {
worker.Arch = v1.ArchitectureARM64
}
if worker.Runtime == "" {
worker.Runtime = v1.RuntimeTart
}
currentTime := time.Now()
if worker.LastSeen.IsZero() {
worker.LastSeen = currentTime
}
worker.CreatedAt = currentTime
return controller.storeUpdate(func(txn storepkg.Transaction) responder.Responder {
// In case there already exist a worker with the same name,
// update it (to avoid overwriting things like SchedulingPaused)
// if the request comes from a worker with the same machine ID
dbWorker, err := txn.GetWorker(worker.Name)
if err != nil {
if errors.Is(err, storepkg.ErrNotFound) {
// Create a new worker
if err := txn.SetWorker(worker); err != nil {
return responder.Error(err)
}
return responder.JSON(200, worker)
}
controller.logger.Errorf("failed to check if the worker "+
"with name %q exists in the DB: %v", worker.Name, err)
return responder.Code(http.StatusInternalServerError)
}
// Update an already existing worker
if worker.MachineID != dbWorker.MachineID {
return responder.JSON(http.StatusConflict, NewErrorResponse("this worker is managed "+
"from a different machine ID, delete this worker first to be able to re-create it"))
}
dbWorker.Arch = worker.Arch
dbWorker.Runtime = worker.Runtime
dbWorker.LastSeen = worker.LastSeen
dbWorker.Resources = worker.Resources
dbWorker.Labels = worker.Labels
dbWorker.DefaultCPU = worker.DefaultCPU
dbWorker.DefaultMemory = worker.DefaultMemory
if err := txn.SetWorker(*dbWorker); err != nil {
return responder.Error(err)
}
return responder.JSON(200, dbWorker)
})
}
func (controller *Controller) updateWorker(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleComputeWrite); responder != nil {
return responder
}
var userWorker v1.Worker
if err := ctx.ShouldBindJSON(&userWorker); err != nil {
return responder.JSON(http.StatusBadRequest, NewErrorResponse("invalid JSON was provided"))
}
return controller.storeUpdate(func(txn storepkg.Transaction) responder.Responder {
dbWorker, err := txn.GetWorker(userWorker.Name)
if err != nil {
return responder.Error(err)
}
dbWorker.LastSeen = userWorker.LastSeen
dbWorker.SchedulingPaused = userWorker.SchedulingPaused
if err := txn.SetWorker(*dbWorker); err != nil {
controller.logger.Errorf("failed to update worker in the DB: %v", err)
return responder.Code(http.StatusInternalServerError)
}
return responder.JSON(200, &dbWorker)
})
}
func (controller *Controller) getWorker(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleComputeRead); responder != nil {
return responder
}
name := ctx.Param("name")
return controller.storeView(func(txn storepkg.Transaction) responder.Responder {
worker, err := txn.GetWorker(name)
if err != nil {
return responder.Error(err)
}
return responder.JSON(http.StatusOK, &worker)
})
}
func (controller *Controller) listWorkers(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleComputeRead); responder != nil {
return responder
}
return controller.storeView(func(txn storepkg.Transaction) responder.Responder {
workers, err := txn.ListWorkers()
if err != nil {
return responder.Error(err)
}
return responder.JSON(http.StatusOK, &workers)
})
}
func (controller *Controller) deleteWorker(ctx *gin.Context) responder.Responder {
if responder := controller.authorize(ctx, v1.ServiceAccountRoleComputeWrite); responder != nil {
return responder
}
name := ctx.Param("name")
return controller.storeUpdate(func(txn storepkg.Transaction) responder.Responder {
if err := txn.DeleteWorker(name); err != nil {
return responder.Error(err)
}
return responder.Code(http.StatusOK)
})
}