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 }