Merge pull request #1539 from hashicorp/f-reap

Adds child process reaping when Consul is running as PID 1.
This commit is contained in:
James Phillips 2015-12-22 10:44:27 -08:00
commit 14922b58ae
4 changed files with 70 additions and 0 deletions

View File

@ -18,6 +18,7 @@ import (
"github.com/armon/go-metrics/datadog"
"github.com/hashicorp/consul/watch"
"github.com/hashicorp/go-checkpoint"
"github.com/hashicorp/go-reap"
"github.com/hashicorp/go-syslog"
"github.com/hashicorp/logutils"
scada "github.com/hashicorp/scada-client"
@ -641,6 +642,33 @@ func (c *Command) Run(args []string) int {
defer server.Shutdown()
}
// Enable child process reaping
if (config.Reap != nil && *config.Reap) || (config.Reap == nil && os.Getpid() == 1) {
if !reap.IsSupported() {
c.Ui.Error("Child process reaping is not supported on this platform (set reap=false)")
return 1
} else {
logger := c.agent.logger
logger.Printf("[DEBUG] Automatically reaping child processes")
pids := make(reap.PidCh, 1)
errors := make(reap.ErrorCh, 1)
go func() {
for {
select {
case pid := <-pids:
logger.Printf("[DEBUG] Reaped child process %d", pid)
case err := <-errors:
logger.Printf("[ERR] Error reaping child process: %v", err)
case <-c.agent.shutdownCh:
return
}
}
}()
go reap.ReapChildren(pids, errors, c.agent.shutdownCh)
}
}
// Check and shut down the SCADA listeners at the end
defer func() {
if c.scadaHttp != nil {

View File

@ -422,6 +422,17 @@ type Config struct {
// Minimum Session TTL
SessionTTLMin time.Duration `mapstructure:"-"`
SessionTTLMinRaw string `mapstructure:"session_ttl_min"`
// Reap controls automatic reaping of child processes, useful if running
// as PID 1 in a Docker container. This defaults to nil which will make
// Consul reap only if it detects it's running as PID 1. If non-nil,
// then this will be used to decide if reaping is enabled.
Reap *bool `mapstructure:"reap"`
}
// Bool is used to initialize bool pointers in struct literals.
func Bool(b bool) *bool {
return &b
}
// UnixSocketPermissions contains information about a unix socket, and
@ -1140,6 +1151,10 @@ func MergeConfig(a, b *Config) *Config {
result.RetryJoinWan = append(result.RetryJoinWan, a.RetryJoinWan...)
result.RetryJoinWan = append(result.RetryJoinWan, b.RetryJoinWan...)
if b.Reap != nil {
result.Reap = b.Reap
}
return &result
}

View File

@ -777,6 +777,27 @@ func TestDecodeConfig(t *testing.T) {
if config.SessionTTLMin != 5*time.Second {
t.Fatalf("bad: %s %#v", config.SessionTTLMin.String(), config)
}
// Reap
input = `{"reap": true}`
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
if err != nil {
t.Fatalf("err: %s", err)
}
if config.Reap == nil || *config.Reap != true {
t.Fatalf("bad: reap not enabled: %#v", config)
}
input = `{}`
config, err = DecodeConfig(bytes.NewReader([]byte(input)))
if err != nil {
t.Fatalf("err: %s", err)
}
if config.Reap != nil {
t.Fatalf("bad: reap not tri-stated: %#v", config)
}
}
func TestDecodeConfig_invalidKeys(t *testing.T) {
@ -1266,6 +1287,7 @@ func TestMergeConfig(t *testing.T) {
RPC: &net.TCPAddr{},
RPCRaw: "127.0.0.5:1233",
},
Reap: Bool(true),
}
c := MergeConfig(a, b)

View File

@ -504,6 +504,11 @@ definitions support being updated during a reload.
* <a name="protocol"></a><a href="#protocol">`protocol`</a> Equivalent to the
[`-protocol` command-line flag](#_protocol).
* <a name="reap"></a><a href="#reap">`reap`</a> controls Consul's automatic reaping of child processes, which
is useful if Consul is running as PID 1 in a Docker container. If this isn't specified, then Consul will
automatically reap child processes if it detects it is running as PID 1. If this is specified, then it
controls reaping regardless of Consul's PID.
* <a name="recursor"></a><a href="#recursor">`recursor`</a> Provides a single recursor address.
This has been deprecated, and the value is appended to the [`recursors`](#recursors) list for
backwards compatibility.