diff --git a/agent/checks/alias.go b/agent/checks/alias.go index 3ee7d62171..1b02842d93 100644 --- a/agent/checks/alias.go +++ b/agent/checks/alias.go @@ -25,9 +25,10 @@ type CheckAlias struct { Node string // Node name of the service. If empty, assumed to be this node. ServiceID string // ID (not name) of the service to alias - CheckID types.CheckID // ID of this check - RPC RPC // Used to query remote server if necessary - Notify CheckNotifier // For updating the check state + CheckID types.CheckID // ID of this check + RPC RPC // Used to query remote server if necessary + RPCReq structs.NodeSpecificRequest // Base request + Notify CheckNotifier // For updating the check state stop bool stopCh chan struct{} @@ -55,7 +56,8 @@ func (c *CheckAlias) Stop() { // run is invoked in a goroutine until Stop() is called. func (c *CheckAlias) run(stopCh chan struct{}) { - args := structs.NodeSpecificRequest{Node: c.Node} + args := c.RPCReq + args.Node = c.Node args.AllowStale = true args.MaxQueryTime = 1 * time.Minute @@ -112,7 +114,12 @@ func (c *CheckAlias) run(stopCh chan struct{}) { msg = "No checks found." } for _, chk := range out.HealthChecks { - if chk.ServiceID != c.ServiceID || chk.Node != c.Node { + if chk.Node != c.Node { + continue + } + + // We allow ServiceID == "" so that we also check node checks + if chk.ServiceID != "" && chk.ServiceID != c.ServiceID { continue } diff --git a/agent/checks/alias_test.go b/agent/checks/alias_test.go index 27f089bb57..431cf116a3 100644 --- a/agent/checks/alias_test.go +++ b/agent/checks/alias_test.go @@ -67,6 +67,55 @@ func TestCheckAlias_remoteNoChecks(t *testing.T) { }) } +// If the node is critical then the check is critical +func TestCheckAlias_remoteNodeFailure(t *testing.T) { + t.Parallel() + + notify := mock.NewNotify() + chkID := types.CheckID("foo") + rpc := &mockRPC{} + chk := &CheckAlias{ + Node: "remote", + ServiceID: "web", + CheckID: chkID, + Notify: notify, + RPC: rpc, + } + + rpc.Reply.Store(structs.IndexedHealthChecks{ + HealthChecks: []*structs.HealthCheck{ + // Should ignore non-matching node + &structs.HealthCheck{ + Node: "A", + ServiceID: "web", + Status: api.HealthCritical, + }, + + // Node failure + &structs.HealthCheck{ + Node: "remote", + ServiceID: "", + Status: api.HealthCritical, + }, + + // Match + &structs.HealthCheck{ + Node: "remote", + ServiceID: "web", + Status: api.HealthPassing, + }, + }, + }) + + chk.Start() + defer chk.Stop() + retry.Run(t, func(r *retry.R) { + if got, want := notify.State(chkID), api.HealthCritical; got != want { + r.Fatalf("got state %q want %q", got, want) + } + }) +} + // Only passing should result in passing func TestCheckAlias_remotePassing(t *testing.T) { t.Parallel()