mirror of https://github.com/status-im/consul.git
agent: notify systemd after JoinLAN (#2121)
This patch adds support for notifying systemd via the NOTIFY_SOCKET by sending 'READY=1' to the socket after a successful JoinLAN. Fixes #2121
This commit is contained in:
parent
37d389f278
commit
31a310f551
|
@ -22,6 +22,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/consul"
|
"github.com/hashicorp/consul/agent/consul"
|
||||||
"github.com/hashicorp/consul/agent/consul/structs"
|
"github.com/hashicorp/consul/agent/consul/structs"
|
||||||
|
"github.com/hashicorp/consul/agent/systemd"
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/consul/ipaddr"
|
"github.com/hashicorp/consul/ipaddr"
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
|
@ -71,6 +72,11 @@ type delegate interface {
|
||||||
Stats() map[string]map[string]string
|
Stats() map[string]map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notifier is called after a successful JoinLAN.
|
||||||
|
type notifier interface {
|
||||||
|
Notify(string) error
|
||||||
|
}
|
||||||
|
|
||||||
// The agent is the long running process that is run on every machine.
|
// The agent is the long running process that is run on every machine.
|
||||||
// It exposes an RPC interface that is used by the CLI to control the
|
// It exposes an RPC interface that is used by the CLI to control the
|
||||||
// agent. The agent runs the query interfaces like HTTP, DNS, and RPC.
|
// agent. The agent runs the query interfaces like HTTP, DNS, and RPC.
|
||||||
|
@ -141,6 +147,9 @@ type Agent struct {
|
||||||
shutdownCh chan struct{}
|
shutdownCh chan struct{}
|
||||||
shutdownLock sync.Mutex
|
shutdownLock sync.Mutex
|
||||||
|
|
||||||
|
// joinLANNotifier is called after a successful JoinLAN.
|
||||||
|
joinLANNotifier notifier
|
||||||
|
|
||||||
// retryJoinCh transports errors from the retry join
|
// retryJoinCh transports errors from the retry join
|
||||||
// attempts.
|
// attempts.
|
||||||
retryJoinCh chan error
|
retryJoinCh chan error
|
||||||
|
@ -198,6 +207,7 @@ func New(c *Config) (*Agent, error) {
|
||||||
checkDockers: make(map[types.CheckID]*CheckDocker),
|
checkDockers: make(map[types.CheckID]*CheckDocker),
|
||||||
eventCh: make(chan serf.UserEvent, 1024),
|
eventCh: make(chan serf.UserEvent, 1024),
|
||||||
eventBuf: make([]*UserEvent, 256),
|
eventBuf: make([]*UserEvent, 256),
|
||||||
|
joinLANNotifier: &systemd.Notifier{},
|
||||||
reloadCh: make(chan chan error),
|
reloadCh: make(chan chan error),
|
||||||
retryJoinCh: make(chan error),
|
retryJoinCh: make(chan error),
|
||||||
shutdownCh: make(chan struct{}),
|
shutdownCh: make(chan struct{}),
|
||||||
|
@ -1216,6 +1226,11 @@ func (a *Agent) JoinLAN(addrs []string) (n int, err error) {
|
||||||
a.logger.Printf("[INFO] agent: (LAN) joining: %v", addrs)
|
a.logger.Printf("[INFO] agent: (LAN) joining: %v", addrs)
|
||||||
n, err = a.delegate.JoinLAN(addrs)
|
n, err = a.delegate.JoinLAN(addrs)
|
||||||
a.logger.Printf("[INFO] agent: (LAN) joined: %d Err: %v", n, err)
|
a.logger.Printf("[INFO] agent: (LAN) joined: %d Err: %v", n, err)
|
||||||
|
if err == nil && a.joinLANNotifier != nil {
|
||||||
|
if notifErr := a.joinLANNotifier.Notify(systemd.Ready); notifErr != nil {
|
||||||
|
a.logger.Printf("[DEBUG] agent: systemd notify failed: ", notifErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -447,6 +447,38 @@ func TestAgent_Join_ACLDeny(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockNotifier struct{ s string }
|
||||||
|
|
||||||
|
func (n *mockNotifier) Notify(state string) error {
|
||||||
|
n.s = state
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgent_JoinLANNotify(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
a1 := NewTestAgent(t.Name(), nil)
|
||||||
|
defer a1.Shutdown()
|
||||||
|
|
||||||
|
cfg2 := TestConfig()
|
||||||
|
cfg2.Server = false
|
||||||
|
cfg2.Bootstrap = false
|
||||||
|
a2 := NewTestAgent(t.Name(), cfg2)
|
||||||
|
defer a2.Shutdown()
|
||||||
|
|
||||||
|
notif := &mockNotifier{}
|
||||||
|
a1.joinLANNotifier = notif
|
||||||
|
|
||||||
|
addr := fmt.Sprintf("127.0.0.1:%d", a2.Config.Ports.SerfLan)
|
||||||
|
_, err := a1.JoinLAN([]string{addr})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got, want := notif.s, "READY=1"; got != want {
|
||||||
|
t.Fatalf("got joinLAN notification %q want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAgent_Leave(t *testing.T) {
|
func TestAgent_Leave(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
a1 := NewTestAgent(t.Name(), nil)
|
a1 := NewTestAgent(t.Name(), nil)
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package systemd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// magic values for systemd
|
||||||
|
// from https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description
|
||||||
|
|
||||||
|
Ready = "READY=1"
|
||||||
|
Reloading = "RELOADING=1"
|
||||||
|
Stopping = "STOPPING=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var NotifyNoSocket = errors.New("No socket")
|
||||||
|
|
||||||
|
// Notifier provides a method to send a message to systemd.
|
||||||
|
type Notifier struct{}
|
||||||
|
|
||||||
|
// Notify sends a message to the init daemon. It is common to ignore the error.
|
||||||
|
func (n *Notifier) Notify(state string) error {
|
||||||
|
addr := &net.UnixAddr{
|
||||||
|
Name: os.Getenv("NOTIFY_SOCKET"),
|
||||||
|
Net: "unixgram",
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr.Name == "" {
|
||||||
|
return NotifyNoSocket
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.DialUnix(addr.Net, nil, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
_, err = conn.Write([]byte(state))
|
||||||
|
return err
|
||||||
|
}
|
|
@ -74,6 +74,11 @@ There are several important messages that [`consul agent`](/docs/commands/agent.
|
||||||
Consul agents in a cluster. Not all Consul agents in a cluster have to
|
Consul agents in a cluster. Not all Consul agents in a cluster have to
|
||||||
use the same port, but this address **MUST** be reachable by all other nodes.
|
use the same port, but this address **MUST** be reachable by all other nodes.
|
||||||
|
|
||||||
|
When running under `systemd` on Linux, Consul notifies systemd by sending
|
||||||
|
`READY=1` to the `$NOTIFY_SOCKET` when a LAN join has completed. For
|
||||||
|
this either the `join` or `retry_join` option has to be set and the
|
||||||
|
service definition file has to have `Type=notify` set.
|
||||||
|
|
||||||
## Stopping an Agent
|
## Stopping an Agent
|
||||||
|
|
||||||
An agent can be stopped in two ways: gracefully or forcefully. To gracefully
|
An agent can be stopped in two ways: gracefully or forcefully. To gracefully
|
||||||
|
|
Loading…
Reference in New Issue