mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 00:22:14 +01:00
166 lines
3.5 KiB
Go
166 lines
3.5 KiB
Go
package pm2
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os/exec"
|
|
|
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xerror"
|
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xexec"
|
|
"github.com/thecodingmachine/gotenberg/internal/pkg/xlog"
|
|
)
|
|
|
|
// Process is a type that can start or
|
|
// stop a process with PM2.
|
|
type Process interface {
|
|
Fullname() string
|
|
Start() error
|
|
IsViable() bool
|
|
Stop() error
|
|
args() []string
|
|
binary() string
|
|
warmup()
|
|
}
|
|
|
|
type pm2Command string
|
|
|
|
const (
|
|
startCommand pm2Command = "start"
|
|
restartCommand pm2Command = "restart"
|
|
stopCommand pm2Command = "stop"
|
|
logsCommand pm2Command = "logs"
|
|
jlistCommand pm2Command = "jlist"
|
|
)
|
|
|
|
/*
|
|
JListItem is a struct
|
|
used for unmarshaling
|
|
ONE item of the result
|
|
of the command "pm2 jlist".
|
|
*/
|
|
type JListItem struct {
|
|
Name string `json:"name"`
|
|
PM2Env struct {
|
|
Status string `json:"status"`
|
|
RestartTime int64 `json:"restart_time"`
|
|
} `json:"pm2_env"`
|
|
Monit struct {
|
|
Memory int64 `json:"memory"`
|
|
CPU float64 `json:"cpu"`
|
|
} `json:"monit"`
|
|
}
|
|
|
|
/*
|
|
JList is a struct
|
|
used for unmarshaling
|
|
the result of the command
|
|
"pm2 jlist".
|
|
*/
|
|
type JList []JListItem
|
|
|
|
func (list JList) isOnline(p Process) bool {
|
|
const onlineStatus string = "online"
|
|
for _, item := range list {
|
|
if item.Name == p.binary() {
|
|
return item.PM2Env.Status == onlineStatus
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// List returns the
|
|
// processes details.
|
|
func List() (*JList, error) {
|
|
const op = "pm2.List"
|
|
resolver := func() (*JList, error) {
|
|
out, err := exec.
|
|
Command("pm2", string(jlistCommand)).
|
|
Output()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data := &JList{}
|
|
if err := json.Unmarshal(out, data); err != nil {
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
list, err := resolver()
|
|
if err != nil {
|
|
return nil, xerror.New(op, err)
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func start(logger xlog.Logger, process Process) error {
|
|
const (
|
|
op string = "pm2.start"
|
|
maximumAttempts int = 3
|
|
)
|
|
resolver := func() error {
|
|
// first, we try to start the process.
|
|
if err := run(logger, startCommand, process); err != nil {
|
|
return err
|
|
}
|
|
// we wait the process to be ready.
|
|
process.warmup()
|
|
// if the process failed to start correctly,
|
|
// we have to restart it.
|
|
if !process.IsViable() {
|
|
attempts := 0
|
|
for attempts < maximumAttempts && !process.IsViable() {
|
|
if err := run(logger, restartCommand, process); err != nil {
|
|
return err
|
|
}
|
|
process.warmup()
|
|
attempts++
|
|
}
|
|
if !process.IsViable() {
|
|
return fmt.Errorf("failed to start '%s'", process.Fullname())
|
|
}
|
|
}
|
|
// the process is viable, let's log its
|
|
// output.
|
|
if err := run(logger, logsCommand, process); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
if err := resolver(); err != nil {
|
|
return xerror.New(op, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func stop(logger xlog.Logger, process Process) error {
|
|
const op string = "pm2.stop"
|
|
if err := run(logger, stopCommand, process); err != nil {
|
|
return xerror.New(op, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func run(logger xlog.Logger, pm2Cmd pm2Command, process Process) error {
|
|
const op string = "pm2.run"
|
|
resolver := func() error {
|
|
args := []string{
|
|
string(pm2Cmd),
|
|
process.binary(),
|
|
}
|
|
if pm2Cmd == startCommand {
|
|
args = append(args, "--interpreter=none", "--")
|
|
args = append(args, process.args()...)
|
|
}
|
|
cmd, err := xexec.Command(logger, "pm2", args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
xexec.LogBeforeExecute(logger, cmd)
|
|
return cmd.Start()
|
|
}
|
|
if err := resolver(); err != nil {
|
|
return xerror.New(op, err)
|
|
}
|
|
return nil
|
|
}
|