add graceful shutdown for bin mode
This commit is contained in:
parent
a183d116a1
commit
6d3d90b0e2
18
_docs/modes.md
Normal file
18
_docs/modes.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
## BIN
|
||||||
|
|
||||||
|
Binary modes just executes any script in shell (`/bin/sh` by default). You can override shell per-unit
|
||||||
|
by `shell: /path/to/shell` configuration param.
|
||||||
|
|
||||||
|
To handle a graceful timeout, child should be able to forward signal: basically, use `exec` before last command.
|
||||||
|
|
||||||
|
Danger (but will work), signals may not be handled by foo
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
command: "V=1 RAIL=2 foo bar -c -y -z"
|
||||||
|
```
|
||||||
|
|
||||||
|
Good
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
command: "V=1 RAIL=2 exec foo bar -c -y -z"
|
||||||
|
```
|
@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@ -33,11 +34,12 @@ func (m *markerResponse) WriteHeader(statusCode int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type binHandler struct {
|
type binHandler struct {
|
||||||
command string
|
command string
|
||||||
workDir string
|
workDir string
|
||||||
shell string
|
shell string
|
||||||
environment []string
|
environment []string
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
gracefulTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bh *binHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
func (bh *binHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||||
@ -75,7 +77,29 @@ func (bh *binHandler) ServeHTTP(writer http.ResponseWriter, request *http.Reques
|
|||||||
cmd.Stdout = marker
|
cmd.Stdout = marker
|
||||||
cmd.Env = env
|
cmd.Env = env
|
||||||
api.SetBinFlags(cmd)
|
api.SetBinFlags(cmd)
|
||||||
|
|
||||||
|
var done bool
|
||||||
|
|
||||||
|
if bh.gracefulTimeout > 0 {
|
||||||
|
graceCtx, graceCancel := context.WithTimeout(ctx, bh.gracefulTimeout)
|
||||||
|
defer graceCancel()
|
||||||
|
go func() {
|
||||||
|
<-graceCtx.Done()
|
||||||
|
proc := cmd.Process
|
||||||
|
if proc == nil || done {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err := proc.Signal(os.Interrupt)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed send signal to process:", err)
|
||||||
|
} else {
|
||||||
|
log.Println("sent graceful shutdown to proces")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
|
done = true
|
||||||
|
|
||||||
if codeReset, ok := writer.(interface{ Status(status int) }); ok && err != nil {
|
if codeReset, ok := writer.(interface{ Status(status int) }); ok && err != nil {
|
||||||
codeReset.Status(http.StatusBadGateway)
|
codeReset.Status(http.StatusBadGateway)
|
||||||
|
File diff suppressed because one or more lines are too long
@ -26,17 +26,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Unit struct {
|
type Unit struct {
|
||||||
Interval time.Duration `yaml:"interval,omitempty"` // interval between attempts
|
Interval time.Duration `yaml:"interval,omitempty"` // interval between attempts
|
||||||
Attempts int `yaml:"attempts,omitempty"` // maximum number of attempts
|
Attempts int `yaml:"attempts,omitempty"` // maximum number of attempts
|
||||||
Workers int `yaml:"workers,omitempty"` // concurrency level - number of parallel requests
|
Workers int `yaml:"workers,omitempty"` // concurrency level - number of parallel requests
|
||||||
Mode string `yaml:"mode,omitempty"` // execution mode: bin, cgi or proxy
|
Mode string `yaml:"mode,omitempty"` // execution mode: bin, cgi or proxy
|
||||||
WorkDir string `yaml:"workdir,omitempty"` // working directory for the worker. if empty - temporary one will generated automatically
|
WorkDir string `yaml:"workdir,omitempty"` // working directory for the worker. if empty - temporary one will generated automatically
|
||||||
Command string `yaml:"command"` // command in a shell to execute
|
Command string `yaml:"command"` // command in a shell to execute
|
||||||
Timeout time.Duration `yaml:"timeout,omitempty"` // maximum execution timeout (enabled only for bin mode and only if positive)
|
Timeout time.Duration `yaml:"timeout,omitempty"` // maximum execution timeout (enabled only for bin mode and only if positive)
|
||||||
Shell string `yaml:"shell,omitempty"` // shell to execute command in bin mode (default - /bin/sh)
|
GracefulTimeout time.Duration `yaml:"graceful_timeout,omitempty"` // maximum execution timeout after which SIGINT will be sent (enabled only for bin mode and only if positive)
|
||||||
Environment map[string]string `yaml:"environment,omitempty"` // custom environment for executable (in addition to system)
|
Shell string `yaml:"shell,omitempty"` // shell to execute command in bin mode (default - /bin/sh)
|
||||||
MaxRequest int64 `yaml:"max_request,omitempty"` // optional maximum HTTP body size (enabled if positive)
|
Environment map[string]string `yaml:"environment,omitempty"` // custom environment for executable (in addition to system)
|
||||||
Authorization struct {
|
MaxRequest int64 `yaml:"max_request,omitempty"` // optional maximum HTTP body size (enabled if positive)
|
||||||
|
Authorization struct {
|
||||||
JWT struct {
|
JWT struct {
|
||||||
Enable bool `yaml:"enable"` // enable JWT verification
|
Enable bool `yaml:"enable"` // enable JWT verification
|
||||||
JWT `yaml:",inline"`
|
JWT `yaml:",inline"`
|
||||||
@ -242,11 +243,12 @@ func (cfg Unit) createRunner() (http.Handler, error) {
|
|||||||
switch cfg.Mode {
|
switch cfg.Mode {
|
||||||
case "bin":
|
case "bin":
|
||||||
return &binHandler{
|
return &binHandler{
|
||||||
command: cfg.Command,
|
command: cfg.Command,
|
||||||
workDir: cfg.WorkDir,
|
workDir: cfg.WorkDir,
|
||||||
shell: cfg.Shell,
|
shell: cfg.Shell,
|
||||||
timeout: cfg.Timeout,
|
timeout: cfg.Timeout,
|
||||||
environment: append(os.Environ(), makeEnvList(cfg.Environment)...),
|
gracefulTimeout: cfg.GracefulTimeout,
|
||||||
|
environment: append(os.Environ(), makeEnvList(cfg.Environment)...),
|
||||||
}, nil
|
}, nil
|
||||||
case "cgi":
|
case "cgi":
|
||||||
return &cgi.Handler{
|
return &cgi.Handler{
|
||||||
|
@ -50,14 +50,25 @@
|
|||||||
<dd class="col-sm-9">{{.Unit.Attempts}}</dd>
|
<dd class="col-sm-9">{{.Unit.Attempts}}</dd>
|
||||||
<dt class="col-sm-3">Interval</dt>
|
<dt class="col-sm-3">Interval</dt>
|
||||||
<dd class="col-sm-9">{{.Unit.Interval}}</dd>
|
<dd class="col-sm-9">{{.Unit.Interval}}</dd>
|
||||||
<dt class="col-sm-3">Timeout</dt>
|
{{if eq .Unit.Mode "bin"}}
|
||||||
<dd class="col-sm-9">
|
<dt class="col-sm-3" title="Timeout after which process will be sent">Timeout</dt>
|
||||||
{{with .Unit.Timeout}}
|
<dd class="col-sm-9">
|
||||||
{{.}}
|
{{with .Unit.Timeout}}
|
||||||
{{else}}
|
{{.}}
|
||||||
∞
|
{{else}}
|
||||||
{{end}}
|
∞
|
||||||
</dd>
|
{{end}}
|
||||||
|
</dd>
|
||||||
|
<dt class="col-sm-3" title="Timeout after which SIGINT will be sent">Graceful timeout
|
||||||
|
</dt>
|
||||||
|
<dd class="col-sm-9">
|
||||||
|
{{with .Unit.GracefulTimeout}}
|
||||||
|
{{.}}
|
||||||
|
{{else}}
|
||||||
|
∞
|
||||||
|
{{end}}
|
||||||
|
</dd>
|
||||||
|
{{end}}
|
||||||
<dt class="col-sm-3">Max request size</dt>
|
<dt class="col-sm-3">Max request size</dt>
|
||||||
<dd class="col-sm-9">
|
<dd class="col-sm-9">
|
||||||
{{with .Unit.MaxRequest}}
|
{{with .Unit.MaxRequest}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user