support configurable termination parameters

This commit is contained in:
Berger Eugene 2022-06-23 01:21:21 +03:00
parent d1b9777230
commit abf1145d46
10 changed files with 101 additions and 21 deletions

2
.gitignore vendored
View File

@ -1,6 +1,6 @@
coverage.out
.vscode
bin/**
bin/process*
*.log
.env
# local build output via go build

View File

@ -149,12 +149,33 @@ process2:
```yaml
process2:
depends_on:
process2:
condition: process_completed_successfully # or "process_started" (default)
process3:
condition: process_completed_successfully # or "process_started" (default)
process3:
condition: process_completed_successfully
```
##### ✅ Termination Parameters
```yaml
process1:
command: "pg_ctl start"
shutdown:
command: "pg_ctl stop"
timeout_seconds: 10 # default 10
signal: 15 # default 15, but only if command is not defined or empty
```
`shutdown` is optional and can be omitted. The default behaviour in this case: `SIGTERM` is issued to the running process.
In case only `shutdown.signal` is defined `[1..31] ` the running process will be terminated with its value.
In case the the `shutdown.command` is defined:
1. The `shutdown.command` is executed with all the Environment Variables of the main process
2. Wait `shutdown.timeout_seconds` for its completion (if not defined wait for 10 seconds)
3. In case of timeout the process will receive the `SIGKILL` signal
#### ✅ <u>Output Handling</u>
##### ✅ Show process name

0
bin/.gitkeep Normal file
View File

View File

@ -11,7 +11,7 @@ processes:
restart: "on-failure"
backoff_seconds: 2
depends_on:
process2:
_process2:
condition: process_completed_successfully
process3:
condition: process_completed
@ -20,7 +20,7 @@ processes:
environment:
- 'EXIT_CODE=0'
process2:
_process2:
command: "./test_loop.bash process2"
log_location: ./pc.proc2.log
availability:
@ -31,7 +31,11 @@ processes:
environment:
- 'ABC=2221'
- 'PRINT_ERR=111'
- 'EXIT_CODE=0'
- 'EXIT_CODE=2'
shutdown:
command: "pkill -f process2"
signal: 15
timeout_seconds: 2
process3:
command: "./test_loop.bash process3"
@ -48,7 +52,7 @@ processes:
# restart: on-failure
environment:
- 'ABC=2221'
- 'EXIT_CODE=1'
- 'EXIT_CODE=4'
kcalc:
command: "kcalc"

View File

@ -24,14 +24,15 @@ type Project struct {
type Processes map[string]ProcessConfig
type ProcessConfig struct {
Name string
Disabled bool `yaml:"disabled,omitempty"`
Command string `yaml:"command"`
LogLocation string `yaml:"log_location,omitempty"`
Environment []string `yaml:"environment,omitempty"`
RestartPolicy RestartPolicyConfig `yaml:"availability,omitempty"`
DependsOn DependsOnConfig `yaml:"depends_on,omitempty"`
Extensions map[string]interface{} `yaml:",inline"`
Name string
Disabled bool `yaml:"disabled,omitempty"`
Command string `yaml:"command"`
LogLocation string `yaml:"log_location,omitempty"`
Environment []string `yaml:"environment,omitempty"`
RestartPolicy RestartPolicyConfig `yaml:"availability,omitempty"`
DependsOn DependsOnConfig `yaml:"depends_on,omitempty"`
ShutDownParams ShutDownParams `yaml:"shutdown,omitempty"`
Extensions map[string]interface{} `yaml:",inline"`
}
type ProcessState struct {
@ -74,6 +75,12 @@ type RestartPolicyConfig struct {
MaxRestarts int `yaml:"max_restarts,omitempty"`
}
type ShutDownParams struct {
ShutDownCommand string `yaml:"command,omitempty"`
ShutDownTimeout int `yaml:"timeout_seconds,omitempty"`
Signal int `yaml:"signal,omitempty"`
}
const (
// ProcessConditionCompleted is the type for waiting until a process has completed (any exit code).
ProcessConditionCompleted = "process_completed"

View File

@ -2,6 +2,7 @@ package app
import (
"bufio"
"context"
"fmt"
"io"
"math/rand"
@ -10,6 +11,7 @@ import (
"runtime"
"strconv"
"sync"
"syscall"
"time"
"github.com/f1bonacc1/process-compose/src/pclog"
@ -18,6 +20,10 @@ import (
"github.com/rs/zerolog/log"
)
const (
DEFAULT_SHUTDOWN_TIMEOUT_SEC = 10
)
type Process struct {
globalEnv []string
procConf ProcessConfig
@ -156,6 +162,33 @@ func (p *Process) WontRun() {
}
func (p *Process) shutDown() error {
if isStringDefined(p.procConf.ShutDownParams.ShutDownCommand) {
return p.doConfiguredStop(p.procConf.ShutDownParams)
}
return p.stop(p.procConf.ShutDownParams.Signal)
}
func (p *Process) doConfiguredStop(params ShutDownParams) error {
timeout := params.ShutDownTimeout
if timeout == 0 {
timeout = DEFAULT_SHUTDOWN_TIMEOUT_SEC
}
log.Debug().Msgf("killing %s with timeout %d ...", p.GetName(), timeout)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, getRunnerShell(), getRunnerArg(), params.ShutDownCommand)
cmd.Env = p.getProcessEnvironment()
if err := cmd.Run(); err != nil {
// the process termination timedout and it will be killed
log.Error().Msgf("killing %s with timeout %d failed", p.GetName(), timeout)
return p.stop(int(syscall.SIGKILL))
}
return nil
}
func (p *Process) onProcessEnd() {
if isStringDefined(p.procConf.LogLocation) {
p.logger.Close()

View File

@ -2,12 +2,22 @@
package app
import "syscall"
import (
"syscall"
)
func (p *Process) stop() error {
const (
min_sig = 1
max_sig = 31
)
func (p *Process) stop(sig int) error {
if sig < min_sig || sig > max_sig {
sig = int(syscall.SIGTERM)
}
pgid, err := syscall.Getpgid(p.cmd.Process.Pid)
if err == nil {
return syscall.Kill(-pgid, syscall.SIGKILL)
return syscall.Kill(-pgid, syscall.Signal(sig))
}
return err
}

View File

@ -5,7 +5,7 @@ import (
"strconv"
)
func (p *Process) stop() error {
func (p *Process) stop(sig int) error {
//p.cmd.Process.Kill()
kill := exec.Command("TASKKILL", "/T", "/F", "/PID", strconv.Itoa(p.cmd.Process.Pid))
return kill.Run()

View File

@ -182,7 +182,7 @@ func (p *Project) StopProcess(name string) error {
log.Error().Msgf("Process %s is not running", name)
return fmt.Errorf("process %s is not running", name)
}
proc.stop()
proc.shutDown()
return nil
}

View File

@ -1,9 +1,14 @@
#!/usr/bin/env bash
#trap "echo ERROR: The program is terminated ; exit" SIGTERM
trap 'echo CODE: $?; exit $EXIT_CODE' 1 2 3 15
LOOPS=30000
for (( i=1; i<=LOOPS; i++ ))
do
sleep 0.01
#sleep 0.01
sleep 0.5
if [[ -z "${PRINT_ERR}" ]]; then
echo "test loop $i loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop loop $1 $ABC"