diff --git a/command/agent/agent.go b/command/agent/agent.go index 5d5e46584f..2824f59e1e 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -107,6 +107,8 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) { return nil, err } + agent.storePid() + return agent, nil } @@ -250,6 +252,8 @@ func (a *Agent) Shutdown() error { err = a.client.Shutdown() } + a.deletePid() + a.logger.Println("[INFO] agent: shutdown complete") a.shutdown = true close(a.shutdownCh) @@ -496,3 +500,36 @@ func (a *Agent) Stats() map[string]map[string]string { } return stats } + +func (a *Agent) storePid() { + pidPath := a.config.PidFile + + if pidPath != "" { + pid := os.Getpid() + pidFile, err := os.OpenFile(pidPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666) + + if err != nil { + fmt.Errorf("Could not open pid file: %v", err) + } + + defer pidFile.Close() + + _, err = pidFile.WriteString(fmt.Sprintf("%d", pid)) + + if err != nil { + fmt.Errorf("Could not write to pid file: %s", err) + } + } +} + +func (a *Agent) deletePid() { + pidPath := a.config.PidFile + + if pidPath != "" { + err := os.Remove(pidPath) + + if err != nil { + fmt.Errorf("Could not remove pid file: %s", err) + } + } +} diff --git a/command/agent/command.go b/command/agent/command.go index 0b59fe9df4..8c3d94b7be 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -50,6 +50,7 @@ func (c *Command) readConfig() *Config { cmdFlags.StringVar(&cmdConfig.Datacenter, "dc", "", "node datacenter") cmdFlags.StringVar(&cmdConfig.DataDir, "data-dir", "", "path to the data directory") cmdFlags.StringVar(&cmdConfig.UiDir, "ui-dir", "", "path to the web UI directory") + cmdFlags.StringVar(&cmdConfig.PidFile, "pid-file", "", "path to file to store PID") cmdFlags.BoolVar(&cmdConfig.Server, "server", false, "run agent as server") cmdFlags.BoolVar(&cmdConfig.Bootstrap, "bootstrap", false, "enable server bootstrap mode") @@ -485,6 +486,7 @@ Options: -protocol=N Sets the protocol version. Defaults to latest. -server Switches agent to server mode. -ui-dir=path Path to directory containing the Web UI resources + -pid-file=path Path to file to store agent PID ` return strings.TrimSpace(helpText) diff --git a/command/agent/config.go b/command/agent/config.go index b098362b48..2cb86cde43 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -128,6 +128,9 @@ type Config struct { // If provided, the UI endpoints will be enabled. UiDir string `mapstructure:"ui_dir"` + // PidFile is the file to store our PID in + PidFile string `mapstructure:"pid_file"` + // AEInterval controls the anti-entropy interval. This is how often // the agent attempts to reconcile it's local state with the server' // representation of our state. Defaults to every 60s. @@ -423,6 +426,9 @@ func MergeConfig(a, b *Config) *Config { if b.UiDir != "" { result.UiDir = b.UiDir } + if b.PidFile != "" { + result.PidFile = b.PidFile + } // Copy the start join addresses result.StartJoin = make([]string, 0, len(a.StartJoin)+len(b.StartJoin)) diff --git a/command/agent/config_test.go b/command/agent/config_test.go index a5ef9f932a..e518b77688 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -257,6 +257,17 @@ func TestDecodeConfig(t *testing.T) { if config.UiDir != "/opt/consul-ui" { t.Fatalf("bad: %#v", config) } + + // Pid File + input = `{"pid_file": "/tmp/consul/pid"}` + config, err = DecodeConfig(bytes.NewReader([]byte(input))) + if err != nil { + t.Fatalf("err: %s", err) + } + + if config.PidFile != "/tmp/consul/pid" { + t.Fatalf("bad: %#v", config) + } } func TestDecodeConfig_Service(t *testing.T) {