Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pid-file flag to agent #128

Merged
merged 3 commits into from
May 6, 2014
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions command/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) {
return nil, err
}

agent.storePid()

return agent, nil
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if this may fail to open today, it may be nice to sanity check to make sure pidPath is not a directory since os.Remove will remove directories. I'm thinking in scenarios like this:

consul agent -data-dir=/tmp/consul -pid-file=./

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put the check in deletePid() before os.Remove happens, since that's what we're actually worried about. Does that seem ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I agree, seeing this error on start would help fixing it right away. I think this would be a totally fine fatal:

fmt.Errorf("Could not write PID file - given path is a directory")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotchya, added check to storePid


if err != nil {
fmt.Errorf("Could not open pid file: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These errors are never sent to stdout.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, good point 😃 It seems like the rest of the functions in agent return an error rather than writing directly to log. It also makes sense that the user would want the agent to error out rather than start up and not save its pid, causing them to possibly lose automatic control and require human intervention to fix.

I'll change it to return an error like everything else in agent.go, unless it is preferred this fails softly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I would prefer to fail hard.

}

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)
}
}
}
2 changes: 2 additions & 0 deletions command/agent/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage info seem over-indented.


`
return strings.TrimSpace(helpText)
Expand Down
6 changes: 6 additions & 0 deletions command/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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))
Expand Down
11 changes: 11 additions & 0 deletions command/agent/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions website/source/docs/agent/options.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ The options below are all specified on the command-line.
* `-ui-dir` - This flag provides a the directory containing the Web UI resources
for Consul. This must be provided to enable the Web UI. Directory must be readable.

* `-pid-file` - This flag provides the file path for the agent to store it's PID. This is useful for
sending signals to the agent, such as `SIGINT` to close it or `SIGHUP` to update check definitions.

## Configuration Files

In addition to the command-line options, configuration can be put into
Expand Down