T3MP3ST public launch build
This commit is contained in:
1 parent
972247b881
commit
184c7d4e59
1589 files changed
+57150
-506
No files matched your search
@@ -0,0 +1,610 @@
|
||||
// Copyright IBM Corp. 2015, 2025
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package nomad
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-msgpack/v2/codec"
|
||||
sframer "github.com/hashicorp/nomad/client/lib/streamframer"
|
||||
cstructs "github.com/hashicorp/nomad/client/structs"
|
||||
"github.com/hashicorp/nomad/command/agent/host"
|
||||
"github.com/hashicorp/nomad/command/agent/monitor"
|
||||
"github.com/hashicorp/nomad/command/agent/pprof"
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/hashicorp/nomad/nomad/peers"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
type Agent struct {
|
||||
srv *Server
|
||||
}
|
||||
|
||||
func NewAgentEndpoint(srv *Server) *Agent {
|
||||
return &Agent{srv: srv}
|
||||
}
|
||||
|
||||
func (a *Agent) register() {
|
||||
a.srv.streamingRpcs.Register("Agent.Monitor", a.monitor)
|
||||
a.srv.streamingRpcs.Register("Agent.MonitorExport", a.monitorExport)
|
||||
}
|
||||
|
||||
func (a *Agent) Profile(args *structs.AgentPprofRequest, reply *structs.AgentPprofResponse) error {
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
a.srv.MeasureRPCRate("agent", structs.RateMetricRead, args)
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Check ACL for agent write
|
||||
aclObj, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !aclObj.AllowAgentWrite() {
|
||||
// we're not checking AllowAgentDebug here because the target might not
|
||||
// be this server, and the server doesn't know if enable_debug has been
|
||||
// set on the target
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Forward to different region if necessary
|
||||
// this would typically be done in a.srv.forward() but since
|
||||
// we are targeting a specific server, not just the leader
|
||||
// we must manually handle region forwarding here.
|
||||
region := args.RequestRegion()
|
||||
if region == "" {
|
||||
return fmt.Errorf("missing target RPC")
|
||||
}
|
||||
|
||||
if region != a.srv.Region() {
|
||||
// Mark that we are forwarding
|
||||
args.SetForwarded()
|
||||
return a.srv.forwardRegion(region, "Agent.Profile", args, reply)
|
||||
}
|
||||
|
||||
// Targeting a node, forward request to node
|
||||
if args.NodeID != "" {
|
||||
return a.forwardProfileClient(args, reply)
|
||||
}
|
||||
|
||||
// Handle serverID not equal to ours
|
||||
if args.ServerID != "" {
|
||||
serverToFwd, err := a.forwardFor(args.ServerID, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if serverToFwd != nil {
|
||||
return a.srv.forwardServer(serverToFwd, "Agent.Profile", args, reply)
|
||||
}
|
||||
}
|
||||
|
||||
// This server is the target, so now we can check for AllowAgentDebug
|
||||
if !aclObj.AllowAgentDebug(a.srv.GetConfig().EnableDebug) {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Process the request on this server
|
||||
var resp []byte
|
||||
var headers map[string]string
|
||||
|
||||
// Determine which profile to run and generate profile.
|
||||
// Blocks for args.Seconds
|
||||
// Our RPC endpoints currently don't support context
|
||||
// or request cancellation so using server shutdownCtx as a
|
||||
// best effort.
|
||||
switch args.ReqType {
|
||||
case pprof.CPUReq:
|
||||
resp, headers, err = pprof.CPUProfile(a.srv.shutdownCtx, args.Seconds)
|
||||
case pprof.CmdReq:
|
||||
resp, headers, err = pprof.Cmdline()
|
||||
case pprof.LookupReq:
|
||||
resp, headers, err = pprof.Profile(args.Profile, args.Debug, args.GC)
|
||||
case pprof.TraceReq:
|
||||
resp, headers, err = pprof.Trace(a.srv.shutdownCtx, args.Seconds)
|
||||
default:
|
||||
err = structs.NewErrRPCCoded(404, "Unknown profile request type")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if pprof.IsErrProfileNotFound(err) {
|
||||
return structs.NewErrRPCCoded(404, err.Error())
|
||||
}
|
||||
return structs.NewErrRPCCoded(500, err.Error())
|
||||
}
|
||||
|
||||
// Copy profile response to reply
|
||||
reply.Payload = resp
|
||||
reply.HTTPHeaders = headers
|
||||
reply.AgentID = a.srv.serf.LocalMember().Name
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Agent) monitor(conn io.ReadWriteCloser) {
|
||||
defer conn.Close()
|
||||
|
||||
// Decode args
|
||||
var args cstructs.MonitorRequest
|
||||
decoder := codec.NewDecoder(conn, structs.MsgpackHandle)
|
||||
encoder := codec.NewEncoder(conn, structs.MsgpackHandle)
|
||||
|
||||
if err := decoder.Decode(&args); err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
authErr := a.srv.Authenticate(nil, &args)
|
||||
a.srv.MeasureRPCRate("agent", structs.RateMetricRead, &args)
|
||||
if authErr != nil {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Check agent read permissions
|
||||
if aclObj, err := a.srv.ResolveACL(&args); err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
} else if !aclObj.AllowAgentRead() {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, pointer.Of(int64(403)), encoder)
|
||||
return
|
||||
}
|
||||
|
||||
logLevel := log.LevelFromString(args.LogLevel)
|
||||
if args.LogLevel == "" {
|
||||
logLevel = log.LevelFromString("INFO")
|
||||
}
|
||||
|
||||
if logLevel == log.NoLevel {
|
||||
handleStreamResultError(errors.New("Unknown log level"), pointer.Of(int64(400)), encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Targeting a node, forward request to node
|
||||
if args.NodeID != "" {
|
||||
a.forwardMonitorClient(conn, args, encoder, decoder, args.NodeID, "Agent.Monitor")
|
||||
// forwarded request has ended, return
|
||||
return
|
||||
}
|
||||
|
||||
region := args.RequestRegion()
|
||||
if region == "" {
|
||||
handleStreamResultError(fmt.Errorf("missing target region"), pointer.Of(int64(400)), encoder)
|
||||
return
|
||||
}
|
||||
if region != a.srv.Region() {
|
||||
// Mark that we are forwarding
|
||||
args.SetForwarded()
|
||||
}
|
||||
|
||||
// Try to forward request to remote region/server
|
||||
if args.ServerID != "" {
|
||||
serverToFwd, err := a.forwardFor(args.ServerID, region)
|
||||
if err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(400)), encoder)
|
||||
return
|
||||
}
|
||||
if serverToFwd != nil {
|
||||
// Empty ServerID to prevent forwarding loop
|
||||
args.ServerID = ""
|
||||
a.forwardMonitorServer(conn, serverToFwd, args, encoder, decoder, "Agent.Monitor")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// NodeID was empty, ServerID was equal to this server, monitor this server
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
m := monitor.New(512, a.srv.logger, &log.LoggerOptions{
|
||||
Level: logLevel,
|
||||
JSONFormat: args.LogJSON,
|
||||
IncludeLocation: args.LogIncludeLocation,
|
||||
})
|
||||
|
||||
frames := make(chan *sframer.StreamFrame, 32)
|
||||
errCh := make(chan error)
|
||||
var buf bytes.Buffer
|
||||
frameCodec := codec.NewEncoder(&buf, structs.JsonHandle)
|
||||
|
||||
framer := sframer.NewStreamFramer(frames, 1*time.Second, 200*time.Millisecond, 1024)
|
||||
framer.Run()
|
||||
defer framer.Destroy()
|
||||
|
||||
// goroutine to detect remote side closing
|
||||
go func() {
|
||||
if _, err := conn.Read(nil); err != nil {
|
||||
// One end of the pipe explicitly closed, exit
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
<-ctx.Done()
|
||||
}()
|
||||
|
||||
logCh := m.Start()
|
||||
defer m.Stop()
|
||||
initialOffset := int64(0)
|
||||
|
||||
// receive logs and build frames
|
||||
go func() {
|
||||
defer framer.Destroy()
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case log := <-logCh:
|
||||
if err := framer.Send("", "log", log, initialOffset); err != nil {
|
||||
select {
|
||||
case errCh <- err:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
break LOOP
|
||||
}
|
||||
case <-ctx.Done():
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
}()
|
||||
streamEncoder := monitor.NewStreamEncoder(&buf, conn, encoder, frameCodec, args.PlainText)
|
||||
streamErr := streamEncoder.EncodeStream(frames, errCh, ctx, framer, false)
|
||||
if streamErr != nil {
|
||||
handleStreamResultError(streamErr, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Agent) monitorExport(conn io.ReadWriteCloser) {
|
||||
defer conn.Close()
|
||||
// Decode args
|
||||
var args cstructs.MonitorExportRequest
|
||||
decoder := codec.NewDecoder(conn, structs.MsgpackHandle)
|
||||
encoder := codec.NewEncoder(conn, structs.MsgpackHandle)
|
||||
|
||||
if err := decoder.Decode(&args); err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
authErr := a.srv.Authenticate(nil, &args)
|
||||
a.srv.MeasureRPCRate("agent", structs.RateMetricRead, &args)
|
||||
if authErr != nil {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Check agent read permissions
|
||||
if aclObj, err := a.srv.ResolveACL(&args); err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
} else if !aclObj.AllowAgentRead() {
|
||||
handleStreamResultError(structs.ErrPermissionDenied, pointer.Of(int64(403)), encoder)
|
||||
return
|
||||
}
|
||||
|
||||
// Targeting a node, forward request to node
|
||||
if args.NodeID != "" {
|
||||
a.forwardMonitorClient(conn, args, encoder, decoder, args.NodeID, "Agent.MonitorExport")
|
||||
// forwarded request has ended, return
|
||||
return
|
||||
}
|
||||
|
||||
region := args.RequestRegion()
|
||||
if region == "" {
|
||||
handleStreamResultError(fmt.Errorf("missing target region"), pointer.Of(int64(400)), encoder)
|
||||
return
|
||||
}
|
||||
if region != a.srv.Region() {
|
||||
// Mark that we are forwarding
|
||||
args.SetForwarded()
|
||||
}
|
||||
|
||||
// Try to forward request to remote region/server
|
||||
if args.ServerID != "" {
|
||||
serverToFwd, err := a.forwardFor(args.ServerID, region)
|
||||
if err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(400)), encoder)
|
||||
return
|
||||
}
|
||||
if serverToFwd != nil {
|
||||
//empty args.ServerID to prevent forwarding loop
|
||||
args.ServerID = ""
|
||||
a.forwardMonitorServer(conn, serverToFwd, args, encoder, decoder, "Agent.MonitorExport")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
nomadLogPath := a.srv.GetConfig().LogFile
|
||||
if args.OnDisk && nomadLogPath == "" {
|
||||
handleStreamResultError(errors.New("No nomad log file defined"), pointer.Of(int64(400)), encoder)
|
||||
}
|
||||
// NodeID was empty, ServerID was equal to this server, monitor this server
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
opts := monitor.MonitorExportOpts{
|
||||
Logger: a.srv.logger,
|
||||
LogsSince: args.LogsSince,
|
||||
ServiceName: args.ServiceName,
|
||||
NomadLogPath: nomadLogPath,
|
||||
OnDisk: args.OnDisk,
|
||||
Follow: args.Follow,
|
||||
Context: ctx,
|
||||
}
|
||||
|
||||
frames := make(chan *sframer.StreamFrame, 32)
|
||||
errCh := make(chan error)
|
||||
var buf bytes.Buffer
|
||||
frameSize := 1024
|
||||
frameCodec := codec.NewEncoder(&buf, structs.JsonHandle)
|
||||
|
||||
framer := sframer.NewStreamFramer(frames, 1*time.Second, 200*time.Millisecond, frameSize)
|
||||
framer.Run()
|
||||
defer framer.Destroy()
|
||||
|
||||
// goroutine to detect remote side closing
|
||||
go func() {
|
||||
if _, err := conn.Read(nil); err != nil {
|
||||
// One end of the pipe explicitly closed, exit
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
<-ctx.Done()
|
||||
}()
|
||||
m, err := monitor.NewExportMonitor(opts)
|
||||
if err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
|
||||
var eofCancelCh chan error
|
||||
|
||||
streamCh := m.Start()
|
||||
initialOffset := int64(0)
|
||||
eofCancel := !opts.Follow
|
||||
|
||||
streamEncoder := monitor.NewStreamEncoder(&buf, conn, encoder, frameCodec, args.PlainText)
|
||||
// receive logs and build frames
|
||||
streamReader := monitor.NewStreamReader(streamCh, framer, int64(frameSize))
|
||||
go func() {
|
||||
defer framer.Destroy()
|
||||
if err := streamReader.StreamFixed(ctx, initialOffset, "", 0, eofCancelCh, eofCancel); err != nil {
|
||||
select {
|
||||
case errCh <- err:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
streamErr := streamEncoder.EncodeStream(frames, errCh, ctx, framer, true)
|
||||
if streamErr != nil {
|
||||
handleStreamResultError(streamErr, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// forwardFor returns a serverParts for a request to be forwarded to.
|
||||
// A response of nil, nil indicates that the current server is equal to the
|
||||
// serverID and region so the request should not be forwarded.
|
||||
func (a *Agent) forwardFor(serverID, region string) (*peers.Parts, error) {
|
||||
var target *peers.Parts
|
||||
var err error
|
||||
|
||||
if serverID == "leader" {
|
||||
isLeader, remoteLeader := a.srv.getLeader()
|
||||
if !isLeader && remoteLeader != nil {
|
||||
target = remoteLeader
|
||||
} else if !isLeader && remoteLeader == nil {
|
||||
return nil, structs.ErrNoLeader
|
||||
} else if isLeader {
|
||||
// This server is current leader do not forward
|
||||
return nil, nil
|
||||
}
|
||||
} else {
|
||||
target, err = a.srv.getServer(region, serverID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Unable to find a server
|
||||
if target == nil {
|
||||
return nil, fmt.Errorf("unknown nomad server %s", serverID)
|
||||
}
|
||||
|
||||
// ServerID is this current server,
|
||||
// No need to forward request
|
||||
if target.Name == a.srv.LocalMember().Name {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func (a *Agent) forwardMonitorClient(conn io.ReadWriteCloser, args any, encoder *codec.Encoder, decoder *codec.Decoder, nodeID string, endpoint string) {
|
||||
// Get the Connection to the client either by fowarding to another server
|
||||
// or creating direct stream
|
||||
state, srv, err := a.findClientConn(nodeID)
|
||||
if err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
|
||||
var clientConn net.Conn
|
||||
|
||||
if state == nil {
|
||||
conn, err := a.srv.streamingRpc(srv, endpoint)
|
||||
if err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
clientConn = conn
|
||||
} else {
|
||||
stream, err := NodeStreamingRpc(state.Session, endpoint)
|
||||
if err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
}
|
||||
clientConn = stream
|
||||
}
|
||||
defer clientConn.Close()
|
||||
|
||||
// Send the Request
|
||||
outEncoder := codec.NewEncoder(clientConn, structs.MsgpackHandle)
|
||||
if err := outEncoder.Encode(args); err != nil {
|
||||
handleStreamResultError(err, nil, encoder)
|
||||
return
|
||||
}
|
||||
|
||||
structs.Bridge(conn, clientConn)
|
||||
}
|
||||
|
||||
func (a *Agent) forwardMonitorServer(conn io.ReadWriteCloser, server *peers.Parts, args any, encoder *codec.Encoder, decoder *codec.Decoder, endpoint string) {
|
||||
serverConn, err := a.srv.streamingRpc(server, "Agent.Monitor")
|
||||
if err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
defer serverConn.Close()
|
||||
|
||||
// Send the Request
|
||||
outEncoder := codec.NewEncoder(serverConn, structs.MsgpackHandle)
|
||||
if err := outEncoder.Encode(args); err != nil {
|
||||
handleStreamResultError(err, pointer.Of(int64(500)), encoder)
|
||||
return
|
||||
}
|
||||
|
||||
structs.Bridge(conn, serverConn)
|
||||
}
|
||||
|
||||
func (a *Agent) forwardProfileClient(args *structs.AgentPprofRequest, reply *structs.AgentPprofResponse) error {
|
||||
state, srv, err := a.findClientConn(args.NodeID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if srv != nil {
|
||||
return a.srv.forwardServer(srv, "Agent.Profile", args, reply)
|
||||
}
|
||||
|
||||
// NodeRpc
|
||||
rpcErr := NodeRpc(state.Session, "Agent.Profile", args, reply)
|
||||
if rpcErr != nil {
|
||||
return rpcErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Host returns data about the agent's host system for the `debug` command.
|
||||
func (a *Agent) Host(args *structs.HostDataRequest, reply *structs.HostDataResponse) error {
|
||||
authErr := a.srv.Authenticate(nil, args)
|
||||
a.srv.MeasureRPCRate("agent", structs.RateMetricRead, args)
|
||||
if authErr != nil {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
aclObj, err := a.srv.ResolveACL(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !aclObj.AllowAgentDebug(a.srv.GetConfig().EnableDebug) {
|
||||
return structs.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Forward to different region if necessary
|
||||
// this would typically be done in a.srv.forward() but since
|
||||
// we are targeting a specific server, not just the leader
|
||||
// we must manually handle region forwarding here.
|
||||
region := args.RequestRegion()
|
||||
if region == "" {
|
||||
return fmt.Errorf("missing target RPC")
|
||||
}
|
||||
|
||||
if region != a.srv.Region() {
|
||||
// Mark that we are forwarding
|
||||
args.SetForwarded()
|
||||
return a.srv.forwardRegion(region, "Agent.Host", args, reply)
|
||||
}
|
||||
|
||||
// Targeting a client node, forward request to node
|
||||
if args.NodeID != "" {
|
||||
client, srv, err := a.findClientConn(args.NodeID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if srv != nil {
|
||||
return a.srv.forwardServer(srv, "Agent.Host", args, reply)
|
||||
}
|
||||
|
||||
return NodeRpc(client.Session, "Agent.Host", args, reply)
|
||||
}
|
||||
|
||||
// Handle serverID not equal to ours
|
||||
if args.ServerID != "" {
|
||||
srv, err := a.forwardFor(args.ServerID, region)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if srv != nil {
|
||||
return a.srv.forwardServer(srv, "Agent.Host", args, reply)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := host.MakeHostData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reply.AgentID = a.srv.serf.LocalMember().Name
|
||||
reply.HostData = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// findClientConn is a helper that returns a connection to the client node or, if the client
|
||||
// is connected to a different server, a serverParts describing the server to which the
|
||||
// client bound RPC should be forwarded.
|
||||
func (a *Agent) findClientConn(nodeID string) (*nodeConnState, *peers.Parts, error) {
|
||||
snap, err := a.srv.State().Snapshot()
|
||||
if err != nil {
|
||||
return nil, nil, structs.NewErrRPCCoded(500, err.Error())
|
||||
}
|
||||
|
||||
node, err := snap.NodeByID(nil, nodeID)
|
||||
if err != nil {
|
||||
return nil, nil, structs.NewErrRPCCoded(500, err.Error())
|
||||
}
|
||||
|
||||
if node == nil {
|
||||
err := fmt.Errorf("Unknown node %q", nodeID)
|
||||
return nil, nil, structs.NewErrRPCCoded(404, err.Error())
|
||||
}
|
||||
|
||||
if err := nodeSupportsRpc(node); err != nil {
|
||||
return nil, nil, structs.NewErrRPCCoded(400, err.Error())
|
||||
}
|
||||
|
||||
// Get the Connection to the client either by fowarding to another server
|
||||
// or creating direct stream
|
||||
state, ok := a.srv.getNodeConn(nodeID)
|
||||
if ok {
|
||||
return state, nil, nil
|
||||
}
|
||||
|
||||
// Determine the server that has a connection to the node
|
||||
srv, err := a.srv.serverWithNodeConn(nodeID, a.srv.Region())
|
||||
if err != nil {
|
||||
code := 500
|
||||
if structs.IsErrNoNodeConn(err) {
|
||||
code = 404
|
||||
}
|
||||
return nil, nil, structs.NewErrRPCCoded(code, err.Error())
|
||||
}
|
||||
|
||||
return nil, srv, nil
|
||||
}
|
||||
Reference in new issue
Block a user