398 lines
11 KiB
Go
398 lines
11 KiB
Go
package controllers
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"nodemaster/models"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
beego "github.com/beego/beego/v2/server/web"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ServicesController manages services on this host.
|
||
|
|
type ServicesController struct {
|
||
|
|
beego.Controller
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title GetAllServices
|
||
|
|
// @Description list all services
|
||
|
|
// @Success 200 {object} []models.Service
|
||
|
|
// @router / [get]
|
||
|
|
func (c *ServicesController) GetAll() {
|
||
|
|
c.Data["json"] = models.GetAllServices()
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title AddService
|
||
|
|
// @Description register a new service
|
||
|
|
// @Param body body models.Service true "service definition"
|
||
|
|
// @Success 201 {object} models.Service
|
||
|
|
// @Failure 400 invalid body
|
||
|
|
// @router / [post]
|
||
|
|
func (c *ServicesController) Post() {
|
||
|
|
var s models.Service
|
||
|
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &s); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(400)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
created, err := models.AddService(s)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(500)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Ctx.Output.SetStatus(201)
|
||
|
|
c.Data["json"] = created
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title GetService
|
||
|
|
// @Description get a service by id
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Success 200 {object} models.Service
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id [get]
|
||
|
|
func (c *ServicesController) Get() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
s, err := models.GetService(id)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = s
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title UpdateService
|
||
|
|
// @Description update a service definition
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Param body body models.Service true "updated service data"
|
||
|
|
// @Success 200 {object} models.Service
|
||
|
|
// @Failure 400 invalid body
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id [put]
|
||
|
|
func (c *ServicesController) Put() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
var s models.Service
|
||
|
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &s); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(400)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
updated, err := models.UpdateService(id, s)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = updated
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title DeleteService
|
||
|
|
// @Description delete a service
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Success 200 {string} delete success
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id [delete]
|
||
|
|
func (c *ServicesController) Delete() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
if err := models.DeleteService(id); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = map[string]string{"message": "delete success"}
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title StartService
|
||
|
|
// @Description start a service via its compose file
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Success 200 {string} started
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/start [post]
|
||
|
|
func (c *ServicesController) Start() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
svc, err := models.GetService(id)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := models.StartService(svc); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(500)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
_ = models.UpdateServiceStatus(id, "up")
|
||
|
|
c.Data["json"] = map[string]string{"message": "service started"}
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title StopService
|
||
|
|
// @Description stop a service via its compose file
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Success 200 {string} stopped
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/stop [post]
|
||
|
|
func (c *ServicesController) Stop() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
svc, err := models.GetService(id)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := models.StopService(svc); err != nil {
|
||
|
|
_ = models.UpdateServiceStatus(id, "down")
|
||
|
|
c.Ctx.Output.SetStatus(500)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
_ = models.UpdateServiceStatus(id, "down")
|
||
|
|
c.Data["json"] = map[string]string{"message": "service stopped"}
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title BackupService
|
||
|
|
// @Description trigger an immediate backup for a service across all targets (asynchronous)
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Success 202 {string} backup started
|
||
|
|
// @Failure 400 no backup configured
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup [post]
|
||
|
|
func (c *ServicesController) Backup() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
svc, err := models.GetService(id)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if svc.Backup == nil || len(svc.Backup.Targets) == 0 {
|
||
|
|
c.Ctx.Output.SetStatus(400)
|
||
|
|
c.Data["json"] = map[string]string{"error": "no backup targets configured for this service"}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.Ctx.Output.SetStatus(202)
|
||
|
|
c.Data["json"] = map[string]string{
|
||
|
|
"message": "backup started",
|
||
|
|
"started": fmt.Sprintf("%s", time.Now().UTC().Format(time.RFC3339)),
|
||
|
|
}
|
||
|
|
c.ServeJSON()
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
_ = models.RunServiceBackup(id, nil)
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title GetServiceBackupTargets
|
||
|
|
// @Description list a service's backup target definitions
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Success 200 {object} []models.BackupTarget
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/targets [get]
|
||
|
|
func (c *ServicesController) GetBackupTargets() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
targets, err := models.GetServiceBackupTargets(id)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = targets
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title AddServiceBackupTarget
|
||
|
|
// @Description define a new backup target for a service
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Param body body models.BackupTarget true "backup target definition"
|
||
|
|
// @Success 201 {object} models.BackupTarget
|
||
|
|
// @Failure 400 invalid body
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/targets [post]
|
||
|
|
func (c *ServicesController) AddBackupTarget() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
var t models.BackupTarget
|
||
|
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &t); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(400)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
created, err := models.AddServiceBackupTarget(id, t)
|
||
|
|
if err != nil {
|
||
|
|
status := 400
|
||
|
|
if err.Error() == "service not found" {
|
||
|
|
status = 404
|
||
|
|
}
|
||
|
|
c.Ctx.Output.SetStatus(status)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Ctx.Output.SetStatus(201)
|
||
|
|
c.Data["json"] = created
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title GetServiceBackupTarget
|
||
|
|
// @Description get a service backup target by id
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Param tid path string true "backup target id"
|
||
|
|
// @Success 200 {object} models.BackupTarget
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/targets/:tid [get]
|
||
|
|
func (c *ServicesController) GetBackupTarget() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
tid := c.Ctx.Input.Param(":tid")
|
||
|
|
t, err := models.GetServiceBackupTarget(id, tid)
|
||
|
|
if err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = t
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title PutServiceBackupTarget
|
||
|
|
// @Description update a service backup target definition
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Param tid path string true "backup target id"
|
||
|
|
// @Param body body models.BackupTarget true "updated backup target"
|
||
|
|
// @Success 200 {object} models.BackupTarget
|
||
|
|
// @Failure 400 invalid body
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/targets/:tid [put]
|
||
|
|
func (c *ServicesController) PutBackupTarget() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
tid := c.Ctx.Input.Param(":tid")
|
||
|
|
var t models.BackupTarget
|
||
|
|
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &t); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(400)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
updated, err := models.UpdateServiceBackupTarget(id, tid, t)
|
||
|
|
if err != nil {
|
||
|
|
status := 400
|
||
|
|
if strings.Contains(err.Error(), "not found") {
|
||
|
|
status = 404
|
||
|
|
}
|
||
|
|
c.Ctx.Output.SetStatus(status)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = updated
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title DeleteServiceBackupTarget
|
||
|
|
// @Description delete a service backup target definition
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Param tid path string true "backup target id"
|
||
|
|
// @Success 200 {string} delete success
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/targets/:tid [delete]
|
||
|
|
func (c *ServicesController) DeleteBackupTarget() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
tid := c.Ctx.Input.Param(":tid")
|
||
|
|
if err := models.DeleteServiceBackupTarget(id, tid); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = map[string]string{"message": "delete success"}
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title RunServiceBackupTarget
|
||
|
|
// @Description trigger an immediate backup for a single service target (asynchronous)
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Param tid path string true "backup target id"
|
||
|
|
// @Success 202 {string} backup started
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/targets/:tid/run [post]
|
||
|
|
func (c *ServicesController) RunBackupTarget() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
tid := c.Ctx.Input.Param(":tid")
|
||
|
|
if _, err := models.GetServiceBackupTarget(id, tid); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Ctx.Output.SetStatus(202)
|
||
|
|
c.Data["json"] = map[string]string{"message": "backup started"}
|
||
|
|
c.ServeJSON()
|
||
|
|
go func() {
|
||
|
|
_ = models.RunServiceBackup(id, []string{tid})
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title GetServiceBackupTargetProgress
|
||
|
|
// @Description get the current/last run state for a service backup target
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Param tid path string true "backup target id"
|
||
|
|
// @Success 200 {object} models.RunState
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/targets/:tid/progress [get]
|
||
|
|
func (c *ServicesController) GetBackupTargetProgress() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
tid := c.Ctx.Input.Param(":tid")
|
||
|
|
if _, err := models.GetServiceBackupTarget(id, tid); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
state, ok := models.GetProgress("svc:"+id, tid)
|
||
|
|
if !ok {
|
||
|
|
state = models.RunState{TargetID: tid}
|
||
|
|
}
|
||
|
|
c.Data["json"] = state
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|
||
|
|
|
||
|
|
// @Title GetServiceBackupProgress
|
||
|
|
// @Description get the current/last run state for every backup target of a service
|
||
|
|
// @Param id path string true "service id"
|
||
|
|
// @Success 200 {object} []models.RunState
|
||
|
|
// @Failure 404 not found
|
||
|
|
// @router /:id/backup/progress [get]
|
||
|
|
func (c *ServicesController) GetBackupProgress() {
|
||
|
|
id := c.Ctx.Input.Param(":id")
|
||
|
|
if _, err := models.GetService(id); err != nil {
|
||
|
|
c.Ctx.Output.SetStatus(404)
|
||
|
|
c.Data["json"] = map[string]string{"error": err.Error()}
|
||
|
|
c.ServeJSON()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
c.Data["json"] = models.GetProgressForScope("svc:" + id)
|
||
|
|
c.ServeJSON()
|
||
|
|
}
|