6677a103c2
This changeset configures the RPC rate metrics that were added in #15515 to all the RPCs that support authenticated HTTP API requests. These endpoints already configured with pre-forwarding authentication in #15870, and a handful of others were done already as part of the proof-of-concept work. So this changeset is entirely copy-and-pasting one method call into a whole mess of handlers. Upcoming PRs will wire up pre-forwarding auth and rate metrics for the remaining set of RPCs that have no API consumers or aren't authenticated, in smaller chunks that can be more thoughtfully reviewed.
87 lines
2.2 KiB
Go
87 lines
2.2 KiB
Go
package nomad
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
metrics "github.com/armon/go-metrics"
|
|
log "github.com/hashicorp/go-hclog"
|
|
nstructs "github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
"github.com/hashicorp/nomad/client/structs"
|
|
)
|
|
|
|
// ClientStats is used to forward RPC requests to the targed Nomad client's
|
|
// ClientStats endpoint.
|
|
type ClientStats struct {
|
|
srv *Server
|
|
logger log.Logger
|
|
}
|
|
|
|
func NewClientStatsEndpoint(srv *Server) *ClientStats {
|
|
return &ClientStats{srv: srv, logger: srv.logger.Named("client_stats")}
|
|
}
|
|
|
|
func (s *ClientStats) Stats(args *nstructs.NodeSpecificRequest, reply *structs.ClientStatsResponse) error {
|
|
|
|
// We only allow stale reads since the only potentially stale information is
|
|
// the Node registration and the cost is fairly high for adding another hope
|
|
// in the forwarding chain.
|
|
args.QueryOptions.AllowStale = true
|
|
authErr := s.srv.Authenticate(nil, args)
|
|
|
|
// Potentially forward to a different region.
|
|
if done, err := s.srv.forward("ClientStats.Stats", args, args, reply); done {
|
|
return err
|
|
}
|
|
s.srv.MeasureRPCRate("client_stats", nstructs.RateMetricRead, args)
|
|
if authErr != nil {
|
|
return nstructs.ErrPermissionDenied
|
|
}
|
|
defer metrics.MeasureSince([]string{"nomad", "client_stats", "stats"}, time.Now())
|
|
|
|
// Check node read permissions
|
|
if aclObj, err := s.srv.ResolveACL(args); err != nil {
|
|
return err
|
|
} else if aclObj != nil && !aclObj.AllowNodeRead() {
|
|
return nstructs.ErrPermissionDenied
|
|
}
|
|
|
|
// Verify the arguments.
|
|
if args.NodeID == "" {
|
|
return errors.New("missing NodeID")
|
|
}
|
|
|
|
// Check if the node even exists and is compatible with NodeRpc
|
|
snap, err := s.srv.State().Snapshot()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Make sure Node is new enough to support RPC
|
|
_, err = getNodeForRpc(snap, args.NodeID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Get the connection to the client
|
|
state, ok := s.srv.getNodeConn(args.NodeID)
|
|
if !ok {
|
|
|
|
// Determine the Server that has a connection to the node.
|
|
srv, err := s.srv.serverWithNodeConn(args.NodeID, s.srv.Region())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if srv == nil {
|
|
return nstructs.ErrNoNodeConn
|
|
}
|
|
|
|
return s.srv.forwardServer(srv, "ClientStats.Stats", args, reply)
|
|
}
|
|
|
|
// Make the RPC
|
|
return NodeRpc(state.Session, "ClientStats.Stats", args, reply)
|
|
}
|