github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/client/agent_endpoint.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"io"
     8  	"time"
     9  
    10  	"github.com/hashicorp/go-msgpack/codec"
    11  	"github.com/hashicorp/nomad/command/agent/host"
    12  	"github.com/hashicorp/nomad/command/agent/monitor"
    13  	"github.com/hashicorp/nomad/command/agent/pprof"
    14  	"github.com/hashicorp/nomad/helper"
    15  	"github.com/hashicorp/nomad/nomad/structs"
    16  
    17  	metrics "github.com/armon/go-metrics"
    18  	log "github.com/hashicorp/go-hclog"
    19  	sframer "github.com/hashicorp/nomad/client/lib/streamframer"
    20  	cstructs "github.com/hashicorp/nomad/client/structs"
    21  )
    22  
    23  type Agent struct {
    24  	c *Client
    25  }
    26  
    27  func NewAgentEndpoint(c *Client) *Agent {
    28  	a := &Agent{c: c}
    29  	a.c.streamingRpcs.Register("Agent.Monitor", a.monitor)
    30  	return a
    31  }
    32  
    33  func (a *Agent) Profile(args *structs.AgentPprofRequest, reply *structs.AgentPprofResponse) error {
    34  	// Check ACL for agent write
    35  	aclObj, err := a.c.ResolveToken(args.AuthToken)
    36  	if err != nil {
    37  		return err
    38  	} else if aclObj != nil && !aclObj.AllowAgentWrite() {
    39  		return structs.ErrPermissionDenied
    40  	}
    41  
    42  	// If ACLs are disabled, EnableDebug must be enabled
    43  	if aclObj == nil && !a.c.config.EnableDebug {
    44  		return structs.ErrPermissionDenied
    45  	}
    46  
    47  	var resp []byte
    48  	var headers map[string]string
    49  
    50  	// Determine which profile to run and generate profile.
    51  	// Blocks for args.Seconds
    52  	// Our RPC endpoints currently don't support context
    53  	// or request cancellation so stubbing with TODO
    54  	switch args.ReqType {
    55  	case pprof.CPUReq:
    56  		resp, headers, err = pprof.CPUProfile(context.TODO(), args.Seconds)
    57  	case pprof.CmdReq:
    58  		resp, headers, err = pprof.Cmdline()
    59  	case pprof.LookupReq:
    60  		resp, headers, err = pprof.Profile(args.Profile, args.Debug, args.GC)
    61  	case pprof.TraceReq:
    62  		resp, headers, err = pprof.Trace(context.TODO(), args.Seconds)
    63  	}
    64  
    65  	if err != nil {
    66  		if pprof.IsErrProfileNotFound(err) {
    67  			return structs.NewErrRPCCoded(404, err.Error())
    68  		}
    69  		return structs.NewErrRPCCoded(500, err.Error())
    70  	}
    71  
    72  	// Copy profile response to reply
    73  	reply.Payload = resp
    74  	reply.AgentID = a.c.NodeID()
    75  	reply.HTTPHeaders = headers
    76  
    77  	return nil
    78  }
    79  
    80  func (a *Agent) monitor(conn io.ReadWriteCloser) {
    81  	defer metrics.MeasureSince([]string{"client", "agent", "monitor"}, time.Now())
    82  	defer conn.Close()
    83  
    84  	// Decode arguments
    85  	var args cstructs.MonitorRequest
    86  	decoder := codec.NewDecoder(conn, structs.MsgpackHandle)
    87  	encoder := codec.NewEncoder(conn, structs.MsgpackHandle)
    88  
    89  	if err := decoder.Decode(&args); err != nil {
    90  		handleStreamResultError(err, helper.Int64ToPtr(500), encoder)
    91  		return
    92  	}
    93  
    94  	// Check acl
    95  	if aclObj, err := a.c.ResolveToken(args.AuthToken); err != nil {
    96  		handleStreamResultError(err, helper.Int64ToPtr(403), encoder)
    97  		return
    98  	} else if aclObj != nil && !aclObj.AllowAgentRead() {
    99  		handleStreamResultError(structs.ErrPermissionDenied, helper.Int64ToPtr(403), encoder)
   100  		return
   101  	}
   102  
   103  	logLevel := log.LevelFromString(args.LogLevel)
   104  	if args.LogLevel == "" {
   105  		logLevel = log.LevelFromString("INFO")
   106  	}
   107  
   108  	if logLevel == log.NoLevel {
   109  		handleStreamResultError(errors.New("Unknown log level"), helper.Int64ToPtr(400), encoder)
   110  		return
   111  	}
   112  
   113  	ctx, cancel := context.WithCancel(context.Background())
   114  	defer cancel()
   115  
   116  	monitor := monitor.New(512, a.c.logger, &log.LoggerOptions{
   117  		JSONFormat: args.LogJSON,
   118  		Level:      logLevel,
   119  	})
   120  
   121  	frames := make(chan *sframer.StreamFrame, streamFramesBuffer)
   122  	errCh := make(chan error)
   123  	var buf bytes.Buffer
   124  	frameCodec := codec.NewEncoder(&buf, structs.JsonHandle)
   125  
   126  	framer := sframer.NewStreamFramer(frames, 1*time.Second, 200*time.Millisecond, 1024)
   127  	framer.Run()
   128  
   129  	defer framer.Destroy()
   130  
   131  	// goroutine to detect remote side closing
   132  	go func() {
   133  		if _, err := conn.Read(nil); err != nil {
   134  			// One end of the pipe explicitly closed, exit
   135  			cancel()
   136  			return
   137  		}
   138  		<-ctx.Done()
   139  	}()
   140  
   141  	logCh := monitor.Start()
   142  	defer monitor.Stop()
   143  	initialOffset := int64(0)
   144  
   145  	// receive logs and build frames
   146  	go func() {
   147  		defer framer.Destroy()
   148  	LOOP:
   149  		for {
   150  			select {
   151  			case log := <-logCh:
   152  				if err := framer.Send("", "log", log, initialOffset); err != nil {
   153  					select {
   154  					case errCh <- err:
   155  					case <-ctx.Done():
   156  					}
   157  					break LOOP
   158  				}
   159  			case <-ctx.Done():
   160  				break LOOP
   161  			}
   162  		}
   163  	}()
   164  
   165  	var streamErr error
   166  OUTER:
   167  	for {
   168  		select {
   169  		case frame, ok := <-frames:
   170  			if !ok {
   171  				// frame may have been closed when an error
   172  				// occurred. Check once more for an error.
   173  				select {
   174  				case streamErr = <-errCh:
   175  					// There was a pending error!
   176  				default:
   177  					// No error, continue on
   178  				}
   179  
   180  				break OUTER
   181  			}
   182  
   183  			var resp cstructs.StreamErrWrapper
   184  			if args.PlainText {
   185  				resp.Payload = frame.Data
   186  			} else {
   187  				if err := frameCodec.Encode(frame); err != nil {
   188  					streamErr = err
   189  					break OUTER
   190  				}
   191  
   192  				resp.Payload = buf.Bytes()
   193  				buf.Reset()
   194  			}
   195  
   196  			if err := encoder.Encode(resp); err != nil {
   197  				streamErr = err
   198  				break OUTER
   199  			}
   200  			encoder.Reset(conn)
   201  		case <-ctx.Done():
   202  			break OUTER
   203  		}
   204  	}
   205  
   206  	if streamErr != nil {
   207  		handleStreamResultError(streamErr, helper.Int64ToPtr(500), encoder)
   208  		return
   209  	}
   210  }
   211  
   212  // Host collects data about the host evironment running the agent
   213  func (a *Agent) Host(args *structs.HostDataRequest, reply *structs.HostDataResponse) error {
   214  	aclObj, err := a.c.ResolveToken(args.AuthToken)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	if (aclObj != nil && !aclObj.AllowAgentRead()) ||
   219  		(aclObj == nil && !a.c.config.EnableDebug) {
   220  		return structs.ErrPermissionDenied
   221  	}
   222  
   223  	data, err := host.MakeHostData()
   224  	if err != nil {
   225  		return err
   226  	}
   227  
   228  	reply.AgentID = a.c.NodeID()
   229  	reply.HostData = data
   230  	return nil
   231  }