feat: fire scheduled workflow runs from the housekeeping leader
This commit is contained in:
@@ -13,7 +13,9 @@ import (
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/auth"
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/services"
|
||||
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/workflowsched"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||
)
|
||||
|
||||
func registerWorkflowRoutes(g *gin.RouterGroup) {
|
||||
@@ -34,6 +36,8 @@ func registerWorkflowRoutes(g *gin.RouterGroup) {
|
||||
g.DELETE("/workflows/:id", deleteWorkflow)
|
||||
g.POST("/workflows/:id/run", runWorkflow)
|
||||
g.GET("/workflows/:id/runs", listWorkflowRuns)
|
||||
g.PUT("/workflows/:id/schedule", putWorkflowSchedule)
|
||||
g.GET("/workflows/:id/schedule/preview", previewWorkflowSchedule)
|
||||
|
||||
g.GET("/runs/:runId", getRun)
|
||||
g.POST("/runs/:runId/cancel", cancelRun)
|
||||
@@ -386,3 +390,51 @@ func cancelRun(c *gin.Context) {
|
||||
services.LogEvent(auth.InstanceID(c), "workflow.run_cancelled", actorFromCtx(c), "", c.Param("runId"), "run cancelled")
|
||||
c.JSON(http.StatusOK, gin.H{"cancelled": true})
|
||||
}
|
||||
|
||||
func putWorkflowSchedule(c *gin.Context) {
|
||||
var body models.Schedule
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"})
|
||||
return
|
||||
}
|
||||
|
||||
instanceID := auth.InstanceID(c)
|
||||
next, err := services.SetSchedule(instanceID, c.Param("id"), &body)
|
||||
if err != nil {
|
||||
if errors.Is(err, workflowsched.ErrBadSchedule) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if errors.Is(err, mongo.ErrNoDocuments) {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "workflow not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
services.LogEvent(instanceID, "workflow.schedule_updated", actorFromCtx(c), "", c.Param("id"),
|
||||
fmt.Sprintf("schedule %q %s enabled=%v", body.Cron, body.TZ, body.Enabled))
|
||||
c.JSON(http.StatusOK, gin.H{"schedule": body, "next_run_at": next})
|
||||
}
|
||||
|
||||
// previewWorkflowSchedule exists so the browser and the scheduler agree on
|
||||
// what a cron string means. A client-side cron parser that disagrees with the
|
||||
// server by one field is a bug found in production, at night.
|
||||
func previewWorkflowSchedule(c *gin.Context) {
|
||||
expr := c.Query("cron")
|
||||
tz := c.Query("tz")
|
||||
|
||||
occurrences := make([]time.Time, 0, 3)
|
||||
from := time.Now()
|
||||
for i := 0; i < 3; i++ {
|
||||
next, err := workflowsched.NextOccurrence(expr, tz, from)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
occurrences = append(occurrences, next)
|
||||
from = next
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"occurrences": occurrences})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user