RoadSign/pkg/sign/pm.go

147 lines
3.1 KiB
Go
Raw Normal View History

2023-12-10 03:30:31 +00:00
package sign
import (
"fmt"
2024-01-01 10:07:21 +00:00
"github.com/samber/lo"
2023-12-10 03:30:31 +00:00
"os"
"os/exec"
"path/filepath"
2024-01-01 10:07:21 +00:00
"strings"
"time"
2023-12-10 03:30:31 +00:00
)
2024-01-01 10:07:21 +00:00
type ProcessStatus = int8
const (
ProcessCreated = ProcessStatus(iota)
ProcessStarting
ProcessStarted
ProcessExited
ProcessFailure
)
type ProcessInstance struct {
ID string `json:"id" yaml:"id"`
Workdir string `json:"workdir" yaml:"workdir"`
Command []string `json:"command" yaml:"command"`
Environment []string `json:"environment" yaml:"environment"`
Prepares [][]string `json:"prepares" yaml:"prepares"`
Preheat bool `json:"preheat" yaml:"preheat"`
2023-12-10 03:30:31 +00:00
2024-01-01 10:07:21 +00:00
Cmd *exec.Cmd `json:"-"`
Logger strings.Builder `json:"-"`
Status ProcessStatus `json:"status"`
Logs string `json:"logs"`
2023-12-10 03:30:31 +00:00
}
2024-01-01 10:07:21 +00:00
func (v *ProcessInstance) BootProcess() error {
2023-12-10 03:30:31 +00:00
if v.Cmd != nil {
return nil
}
2023-12-12 13:07:05 +00:00
if err := v.PrepareProcess(); err != nil {
2023-12-10 03:30:31 +00:00
return err
}
if v.Cmd == nil {
return v.StartProcess()
}
if v.Cmd.Process == nil || v.Cmd.ProcessState == nil {
return v.StartProcess()
}
if v.Cmd.ProcessState.Exited() {
return v.StartProcess()
} else if v.Cmd.ProcessState.Exited() {
return fmt.Errorf("process already dead")
}
if v.Cmd.ProcessState.Exited() {
return fmt.Errorf("cannot start process")
} else {
return nil
}
}
2024-01-01 10:07:21 +00:00
func (v *ProcessInstance) PrepareProcess() error {
2023-12-10 03:30:31 +00:00
for _, script := range v.Prepares {
if len(script) <= 0 {
continue
}
cmd := exec.Command(script[0], script[1:]...)
cmd.Dir = filepath.Join(v.Workdir)
if err := cmd.Run(); err != nil {
return err
}
}
return nil
}
2024-01-01 10:07:21 +00:00
func (v *ProcessInstance) StartProcess() error {
2023-12-10 03:30:31 +00:00
if len(v.Command) <= 0 {
return fmt.Errorf("you need set the command for %s to enable process manager", v.ID)
}
v.Cmd = exec.Command(v.Command[0], v.Command[1:]...)
v.Cmd.Dir = filepath.Join(v.Workdir)
v.Cmd.Env = append(v.Cmd.Env, v.Environment...)
2024-01-01 10:07:21 +00:00
v.Cmd.Stdout = &v.Logger
v.Cmd.Stderr = &v.Logger
// Monitor
go func() {
for {
if v.Cmd.Process == nil || v.Cmd.ProcessState == nil {
v.Status = ProcessStarting
} else if !v.Cmd.ProcessState.Exited() {
v.Status = ProcessStarted
} else {
v.Status = lo.Ternary(v.Cmd.ProcessState.Success(), ProcessExited, ProcessFailure)
return
}
time.Sleep(100 * time.Millisecond)
}
}()
2023-12-10 03:30:31 +00:00
return v.Cmd.Start()
}
2024-01-01 10:07:21 +00:00
func (v *ProcessInstance) StopProcess() error {
2023-12-10 06:52:00 +00:00
if v.Cmd != nil && v.Cmd.Process != nil {
2023-12-10 10:26:04 +00:00
if err := v.Cmd.Process.Signal(os.Interrupt); err != nil {
v.Cmd.Process.Kill()
return err
} else {
v.Cmd = nil
}
2023-12-10 06:52:00 +00:00
}
2023-12-10 10:26:04 +00:00
return nil
2023-12-10 03:30:31 +00:00
}
2023-12-12 13:07:05 +00:00
2024-01-01 10:07:21 +00:00
func (v *ProcessInstance) GetLogs() string {
v.Logs = v.Logger.String()
return v.Logs
}
2023-12-13 11:45:26 +00:00
func (v *RoadApp) PreheatProcesses(callbacks ...func(total int, success int)) {
2024-01-01 10:07:21 +00:00
var processes []*ProcessInstance
2023-12-12 13:07:05 +00:00
for _, site := range v.Sites {
for _, process := range site.Processes {
if process.Preheat {
processes = append(processes, process)
}
}
}
2023-12-13 05:16:00 +00:00
success := 0
2023-12-12 13:07:05 +00:00
for _, process := range processes {
2023-12-13 05:16:00 +00:00
if process.BootProcess() == nil {
success++
}
2023-12-12 13:07:05 +00:00
}
2023-12-13 05:16:00 +00:00
2023-12-13 11:45:26 +00:00
if len(callbacks) > 0 {
for _, callback := range callbacks {
callback(len(processes), success)
}
}
2023-12-12 13:07:05 +00:00
}