mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-16 04:12:16 +01:00
huge refactoring
This commit is contained in:
@@ -1,124 +1,102 @@
|
||||
package pm2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/thecodingmachine/gotenberg/internal/pkg/logger"
|
||||
"github.com/thecodingmachine/gotenberg/internal/pkg/standarderror"
|
||||
)
|
||||
|
||||
const (
|
||||
stoppedState int32 = iota
|
||||
runningState
|
||||
errorState
|
||||
"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
|
||||
// shutdown a process with PM2.
|
||||
// stop a process with PM2.
|
||||
type Process interface {
|
||||
Fullname() string
|
||||
Start() error
|
||||
Shutdown() error
|
||||
IsViable() bool
|
||||
Stop() error
|
||||
args() []string
|
||||
name() string
|
||||
viable() bool
|
||||
binary() string
|
||||
warmup()
|
||||
}
|
||||
|
||||
type processManager struct {
|
||||
heuristicState int32
|
||||
logger *logger.Logger
|
||||
}
|
||||
type pm2Command string
|
||||
|
||||
func (m *processManager) start(p Process) error {
|
||||
const op string = "pm2.start"
|
||||
if err := m.pm2(p, "start"); err != nil {
|
||||
return &standarderror.Error{Op: op, Err: err}
|
||||
}
|
||||
p.warmup()
|
||||
if !p.viable() {
|
||||
attempts := 0
|
||||
for attempts < 5 && !p.viable() {
|
||||
if err := m.pm2(p, "restart"); err != nil {
|
||||
m.heuristicState = errorState
|
||||
return &standarderror.Error{Op: op, Err: err}
|
||||
}
|
||||
p.warmup()
|
||||
attempts++
|
||||
const (
|
||||
startCommand pm2Command = "start"
|
||||
restartCommand pm2Command = "restart"
|
||||
stopCommand pm2Command = "stop"
|
||||
logsCommand pm2Command = "logs"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
if !p.viable() {
|
||||
m.heuristicState = errorState
|
||||
return &standarderror.Error{
|
||||
Op: op,
|
||||
Message: fmt.Sprintf("failed to launch %s", p.Fullname()),
|
||||
// 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
m.heuristicState = runningState
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *processManager) shutdown(p Process) error {
|
||||
const op string = "pm2.shutdown"
|
||||
if m.heuristicState != runningState {
|
||||
// the process is viable, let's log its
|
||||
// output.
|
||||
if err := run(logger, logsCommand, process); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := m.pm2(p, "stop"); err != nil {
|
||||
m.heuristicState = errorState
|
||||
return &standarderror.Error{Op: op, Err: err}
|
||||
if err := resolver(); err != nil {
|
||||
return xerror.New(op, err)
|
||||
}
|
||||
m.heuristicState = stoppedState
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *processManager) pm2(p Process, cmdName string) error {
|
||||
const op string = "pm2.pm2"
|
||||
cmdArgs := []string{
|
||||
cmdName,
|
||||
p.name(),
|
||||
}
|
||||
if cmdName == "start" {
|
||||
cmdArgs = append(cmdArgs, "--interpreter=none", "--")
|
||||
cmdArgs = append(cmdArgs, p.args()...)
|
||||
}
|
||||
cmd := exec.Command(
|
||||
"pm2",
|
||||
cmdArgs...,
|
||||
)
|
||||
m.logger.DebugfOp(op, "executing command: %s", strings.Join(cmd.Args, " "))
|
||||
processStdOut, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return &standarderror.Error{Op: op, Err: err}
|
||||
}
|
||||
processStdErr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return &standarderror.Error{Op: op, Err: err}
|
||||
}
|
||||
readFromPipe := func(outputType string, reader io.ReadCloser) {
|
||||
readFromPipeOp := fmt.Sprintf("pm2.%s.%s", p.name(), outputType)
|
||||
r := bufio.NewReader(reader)
|
||||
defer reader.Close() // nolint: errcheck
|
||||
for {
|
||||
line, _, err := r.ReadLine()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
m.logger.ErrorOp(readFromPipeOp, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
if len(line) != 0 {
|
||||
m.logger.DebugfOp(readFromPipeOp, string(line))
|
||||
}
|
||||
}
|
||||
}
|
||||
go readFromPipe("stdout", processStdOut)
|
||||
go readFromPipe("stderr", processStdErr)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return &standarderror.Error{Op: op, Err: err}
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user