2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2015-06-03 10:26:50 +00:00
|
|
|
package nomad
|
|
|
|
|
2017-10-10 17:36:54 +00:00
|
|
|
import (
|
2018-09-15 23:23:13 +00:00
|
|
|
"errors"
|
2017-12-18 21:16:23 +00:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
|
2022-12-01 15:05:15 +00:00
|
|
|
"github.com/hashicorp/go-hclog"
|
2018-01-13 00:52:24 +00:00
|
|
|
|
2017-10-10 17:36:54 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
)
|
2015-08-15 19:59:10 +00:00
|
|
|
|
2015-06-03 10:26:50 +00:00
|
|
|
// Status endpoint is used to check on server status
|
|
|
|
type Status struct {
|
2018-09-15 23:23:13 +00:00
|
|
|
srv *Server
|
2022-12-01 15:05:15 +00:00
|
|
|
ctx *RPCContext
|
|
|
|
logger hclog.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewStatusEndpoint(srv *Server, ctx *RPCContext) *Status {
|
|
|
|
return &Status{srv: srv, ctx: ctx, logger: srv.logger.Named("status")}
|
2015-08-15 19:59:10 +00:00
|
|
|
}
|
|
|
|
|
2015-06-03 10:26:50 +00:00
|
|
|
// Ping is used to just check for connectivity
|
2023-01-24 15:52:07 +00:00
|
|
|
func (s *Status) Ping(args structs.GenericRequest, reply *struct{}) error {
|
|
|
|
// note: we're intentionally throwing away any auth error here and only
|
|
|
|
// authenticate so that we can measure rate metrics
|
|
|
|
s.srv.Authenticate(s.ctx, &args)
|
2023-01-26 20:05:51 +00:00
|
|
|
s.srv.MeasureRPCRate("status", structs.RateMetricRead, &args)
|
2015-06-03 10:26:50 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leader is used to get the address of the leader
|
2015-09-07 01:07:05 +00:00
|
|
|
func (s *Status) Leader(args *structs.GenericRequest, reply *string) error {
|
2023-01-26 20:05:51 +00:00
|
|
|
// note: we're intentionally throwing away any auth error here and only
|
|
|
|
// authenticate so that we can measure rate metrics
|
|
|
|
s.srv.Authenticate(s.ctx, args)
|
|
|
|
s.srv.MeasureRPCRate("status", structs.RateMetricRead, args)
|
|
|
|
|
2015-09-07 01:07:05 +00:00
|
|
|
if args.Region == "" {
|
|
|
|
args.Region = s.srv.config.Region
|
|
|
|
}
|
|
|
|
if done, err := s.srv.forward("Status.Leader", args, args, reply); done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-03 00:07:15 +00:00
|
|
|
leader := string(s.srv.raft.Leader())
|
2015-06-03 10:26:50 +00:00
|
|
|
if leader != "" {
|
|
|
|
*reply = leader
|
|
|
|
} else {
|
|
|
|
*reply = ""
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Peers is used to get all the Raft peers
|
2015-09-07 01:07:05 +00:00
|
|
|
func (s *Status) Peers(args *structs.GenericRequest, reply *[]string) error {
|
2023-01-26 20:05:51 +00:00
|
|
|
// note: we're intentionally throwing away any auth error here and only
|
|
|
|
// authenticate so that we can measure rate metrics
|
|
|
|
s.srv.Authenticate(s.ctx, args)
|
|
|
|
s.srv.MeasureRPCRate("status", structs.RateMetricList, args)
|
|
|
|
|
2017-02-08 22:50:19 +00:00
|
|
|
if args.Region == "" {
|
|
|
|
args.Region = s.srv.config.Region
|
|
|
|
}
|
2015-09-07 01:07:05 +00:00
|
|
|
if done, err := s.srv.forward("Status.Peers", args, args, reply); done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-03 00:07:15 +00:00
|
|
|
future := s.srv.raft.GetConfiguration()
|
|
|
|
if err := future.Error(); err != nil {
|
2015-06-03 10:26:50 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-02-03 00:07:15 +00:00
|
|
|
for _, server := range future.Configuration().Servers {
|
|
|
|
*reply = append(*reply, string(server.Address))
|
|
|
|
}
|
2015-06-03 10:26:50 +00:00
|
|
|
return nil
|
|
|
|
}
|
2016-11-03 21:14:52 +00:00
|
|
|
|
|
|
|
// Members return the list of servers in a cluster that a particular server is
|
|
|
|
// aware of
|
|
|
|
func (s *Status) Members(args *structs.GenericRequest, reply *structs.ServerMembersResponse) error {
|
2023-01-25 19:33:06 +00:00
|
|
|
authErr := s.srv.Authenticate(s.ctx, args)
|
2023-01-26 20:05:51 +00:00
|
|
|
s.srv.MeasureRPCRate("status", structs.RateMetricList, args)
|
2023-01-25 19:33:06 +00:00
|
|
|
if authErr != nil {
|
|
|
|
return structs.ErrPermissionDenied
|
|
|
|
}
|
2017-10-10 17:36:54 +00:00
|
|
|
// Check node read permissions
|
2023-01-25 19:33:06 +00:00
|
|
|
if aclObj, err := s.srv.ResolveACL(args); err != nil {
|
2017-10-10 17:36:54 +00:00
|
|
|
return err
|
|
|
|
} else if aclObj != nil && !aclObj.AllowNodeRead() {
|
|
|
|
return structs.ErrPermissionDenied
|
|
|
|
}
|
|
|
|
|
2016-11-03 21:14:52 +00:00
|
|
|
serfMembers := s.srv.Members()
|
|
|
|
members := make([]*structs.ServerMember, len(serfMembers))
|
|
|
|
for i, mem := range serfMembers {
|
|
|
|
members[i] = &structs.ServerMember{
|
|
|
|
Name: mem.Name,
|
|
|
|
Addr: mem.Addr,
|
|
|
|
Port: mem.Port,
|
|
|
|
Tags: mem.Tags,
|
|
|
|
Status: mem.Status.String(),
|
|
|
|
ProtocolMin: mem.ProtocolMin,
|
|
|
|
ProtocolMax: mem.ProtocolMax,
|
|
|
|
ProtocolCur: mem.ProtocolCur,
|
|
|
|
DelegateMin: mem.DelegateMin,
|
|
|
|
DelegateMax: mem.DelegateMax,
|
|
|
|
DelegateCur: mem.DelegateCur,
|
|
|
|
}
|
|
|
|
}
|
2016-11-06 18:29:09 +00:00
|
|
|
*reply = structs.ServerMembersResponse{
|
2016-11-03 21:14:52 +00:00
|
|
|
ServerName: s.srv.config.NodeName,
|
|
|
|
ServerRegion: s.srv.config.Region,
|
|
|
|
ServerDC: s.srv.config.Datacenter,
|
|
|
|
Members: members,
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-12-18 21:16:23 +00:00
|
|
|
|
2021-08-30 09:08:12 +00:00
|
|
|
// RaftStats is used by Autopilot to query the raft stats of the local server.
|
2023-01-26 20:05:51 +00:00
|
|
|
func (s *Status) RaftStats(args *structs.GenericRequest, reply *structs.RaftStats) error {
|
|
|
|
// note: we're intentionally throwing away any auth error here and only
|
|
|
|
// authenticate so that we can measure rate metrics
|
|
|
|
s.srv.Authenticate(s.ctx, args)
|
|
|
|
s.srv.MeasureRPCRate("status", structs.RateMetricRead, args)
|
|
|
|
|
2017-12-18 21:16:23 +00:00
|
|
|
stats := s.srv.raft.Stats()
|
|
|
|
|
|
|
|
var err error
|
|
|
|
reply.LastContact = stats["last_contact"]
|
|
|
|
reply.LastIndex, err = strconv.ParseUint(stats["last_log_index"], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error parsing server's last_log_index value: %s", err)
|
|
|
|
}
|
|
|
|
reply.LastTerm, err = strconv.ParseUint(stats["last_log_term"], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error parsing server's last_log_term value: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2018-01-13 00:52:24 +00:00
|
|
|
|
|
|
|
// HasNodeConn returns whether the server has a connection to the requested
|
|
|
|
// Node.
|
|
|
|
func (s *Status) HasNodeConn(args *structs.NodeSpecificRequest, reply *structs.NodeConnQueryResponse) error {
|
2023-01-26 20:05:51 +00:00
|
|
|
// note: we're intentionally throwing away any auth error here and only
|
|
|
|
// authenticate so that we can measure rate metrics
|
|
|
|
s.srv.Authenticate(s.ctx, args)
|
|
|
|
s.srv.MeasureRPCRate("status", structs.RateMetricRead, args)
|
|
|
|
|
2018-01-13 00:52:24 +00:00
|
|
|
// Validate the args
|
|
|
|
if args.NodeID == "" {
|
|
|
|
return errors.New("Must provide the NodeID")
|
|
|
|
}
|
|
|
|
|
|
|
|
state, ok := s.srv.getNodeConn(args.NodeID)
|
|
|
|
if ok {
|
|
|
|
reply.Connected = true
|
|
|
|
reply.Established = state.Established
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|