Files
nomad/command/agent/sdnotify_linux.go
Tim Gross 54fc146432 agent: add support for sdnotify protocol (#20528)
Nomad agents expect to receive `SIGHUP` to reload their configuration. The
signal handler for this is installed fairly late in agent startup, after the
client or server components are up and running. This means that configuration
management tools can potentially reload the configuration before the agent can
handle it, causing the agent to crash.

We don't want to allow configuration reload during client or server component
startup, because it would significantly complicate initialization. Instead,
we'll implement the systemd notify protocol. This causes systemd to block
sending configuration reload signals until the agent is actually ready. Users
can still bypass this by sending signals directly.

Note that there are several Go libraries that implement the sdnotify protocol,
but most are part of much larger projects which would create a lot of dependabot
burden. The bits of the protocol we need are extremely simple to implement in a
just a couple of functions.

For non-Linux or non-systemd Linux systems, this feature is a no-op. In future
work we could potentially implement service notification for Windows as well.

Fixes: https://github.com/hashicorp/nomad/issues/3885
2024-05-03 13:42:07 -04:00

41 lines
1004 B
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
//go:build linux
package agent
import (
"io"
"net"
"os"
"time"
)
const sdNotifySocketEnvVar = "NOTIFY_SOCKET"
// openNotify opens the systemd notify socket only if the expected env var has
// been set, because the systemd unit file is Type=notify or Type=notify-reload
// (systemd 253+). It then unsets the env var in the agent process so that child
// processes can't accidentally inherit it. This function returns (nil, nil) if
// the env var isn't set.
func openNotify() (io.WriteCloser, error) {
socketPath := os.Getenv(sdNotifySocketEnvVar)
if socketPath == "" {
return nil, nil
}
defer os.Unsetenv(sdNotifySocketEnvVar)
conn, err := net.DialTimeout("unixgram", socketPath, time.Second)
return conn, err
}
// sdNotify sends the message on the systemd notify socket, and gracefully
// handles a nil socket
func sdNotify(w io.Writer, msg string) {
if w == nil || msg == "" {
return
}
w.Write([]byte(msg))
}