add NoRestartOn config parameter

This commit is contained in:
Brian Picciano 2022-02-24 20:48:37 -07:00
parent 20cb7b9255
commit 2c5c669c72
2 changed files with 28 additions and 12 deletions

View File

@ -19,6 +19,8 @@ processes:
env: env:
TARGET: example.com TARGET: example.com
dir: "/tmp"
# pmux uses an exponential backoff when restarting a process, so subsequent # pmux uses an exponential backoff when restarting a process, so subsequent
# restarts will each take longer and longer. minWait/maxWait indicate the # restarts will each take longer and longer. minWait/maxWait indicate the
# min/max wait times between restarts of this process, respectively. # min/max wait times between restarts of this process, respectively.

View File

@ -27,7 +27,7 @@ type Config struct {
// Dir is the directory the process will be run in. If not set then the // Dir is the directory the process will be run in. If not set then the
// process is run in the same directory as this parent process. // process is run in the same directory as this parent process.
Dir string Dir string `yaml:"dir"`
// MinWait and MaxWait are the minimum and maximum amount of time between // MinWait and MaxWait are the minimum and maximum amount of time between
// restarts that RunProcess will wait. // restarts that RunProcess will wait.
@ -42,6 +42,10 @@ type Config struct {
// //
// Defalts to 10 seconds. // Defalts to 10 seconds.
SigKillWait time.Duration `yaml:"sigKillWait"` SigKillWait time.Duration `yaml:"sigKillWait"`
// NoRestartOn indicates which exit codes should result in the process not
// being restarted any further.
NoRestartOn []int `yaml:"no_restart_on"`
} }
func (cfg Config) withDefaults() Config { func (cfg Config) withDefaults() Config {
@ -63,14 +67,15 @@ func (cfg Config) withDefaults() Config {
// RunProcessOnce runs the process described by the Config (though it doesn't // RunProcessOnce runs the process described by the Config (though it doesn't
// use all fields from the Config). The process is killed if the context is // use all fields from the Config). The process is killed if the context is
// canceled. // canceled. The exit status of the process is returned, or -1 if the process
// was never started.
// //
// It returns nil if the process exits normally with a zero status. It returns // It returns nil if the process exits normally with a zero status. It returns
// an error otherwise. // an error otherwise.
// //
// The stdout and stderr of the process will be written to the given Logger, as // The stdout and stderr of the process will be written to the given Logger, as
// well as various runtime events. // well as various runtime events.
func RunProcessOnce(ctx context.Context, logger Logger, cfg Config) error { func RunProcessOnce(ctx context.Context, logger Logger, cfg Config) (int, error) {
cfg = cfg.withDefaults() cfg = cfg.withDefaults()
@ -106,13 +111,13 @@ func RunProcessOnce(ctx context.Context, logger Logger, cfg Config) error {
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
return fmt.Errorf("getting stdout pipe: %w", err) return -1, fmt.Errorf("getting stdout pipe: %w", err)
} }
defer stdout.Close() defer stdout.Close()
stderr, err := cmd.StderrPipe() stderr, err := cmd.StderrPipe()
if err != nil { if err != nil {
return fmt.Errorf("getting stderr pipe: %w", err) return -1, fmt.Errorf("getting stderr pipe: %w", err)
} }
defer stderr.Close() defer stderr.Close()
@ -120,7 +125,7 @@ func RunProcessOnce(ctx context.Context, logger Logger, cfg Config) error {
fwdOutPipe(stderr) fwdOutPipe(stderr)
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return fmt.Errorf("starting process: %w", err) return -1, fmt.Errorf("starting process: %w", err)
} }
// go-routine which will sent interrupt if the context is cancelled. Also // go-routine which will sent interrupt if the context is cancelled. Also
@ -148,11 +153,14 @@ func RunProcessOnce(ctx context.Context, logger Logger, cfg Config) error {
wg.Wait() wg.Wait()
if err := cmd.Wait(); err != nil { err = cmd.Wait()
return fmt.Errorf("process exited: %w", err) exitCode := cmd.ProcessState.ExitCode()
if err != nil {
return exitCode, fmt.Errorf("process exited: %w", err)
} }
return nil return exitCode, nil
} }
// RunProcess is a process (configured by Config) until the context is canceled, // RunProcess is a process (configured by Config) until the context is canceled,
@ -172,19 +180,25 @@ func RunProcess(ctx context.Context, logger Logger, cfg Config) {
for { for {
start := time.Now() start := time.Now()
err := RunProcessOnce(ctx, logger, cfg) exitCode, err := RunProcessOnce(ctx, logger, cfg)
took := time.Since(start) took := time.Since(start)
if err != nil { if err != nil {
logger.Printf("%v", err) logger.Printf("exit code %d, %v", exitCode, err)
} else { } else {
logger.Println("exit status 0") logger.Println("exit code 0")
} }
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return return
} }
for i := range cfg.NoRestartOn {
if cfg.NoRestartOn[i] == exitCode {
return
}
}
wait = ((wait * 2) - took).Truncate(time.Millisecond) wait = ((wait * 2) - took).Truncate(time.Millisecond)
if wait < cfg.MinWait { if wait < cfg.MinWait {