Skip to content

Commit

Permalink
🧹 Add otel spans so that we can record error messages (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
jaym authored Feb 23, 2024
1 parent 6f3b961 commit 24129d4
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 16 deletions.
8 changes: 7 additions & 1 deletion error.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import (
"github.com/rs/zerolog/log"
"go.mondoo.com/ranger-rpc/codes"
"go.mondoo.com/ranger-rpc/status"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/protobuf/proto"
)

// HttpError writes an error to the response.
func HttpError(w http.ResponseWriter, req *http.Request, err error) {
func HttpError(span trace.Span, w http.ResponseWriter, req *http.Request, err error) {
// check if the accept header is set, otherwise use the incoming content type
accept := determineResponseType(req.Header.Get("Content-Type"), req.Header.Get("Accept"))

Expand All @@ -29,6 +31,10 @@ func HttpError(w http.ResponseWriter, req *http.Request, err error) {
// write status code
status := status.HTTPStatusFromCode(s.Code())

span.RecordError(err, trace.WithAttributes(
attribute.Int("mondoo.ranger.status", status),
))

if status >= 500 {
log.Error().
Err(err).
Expand Down
14 changes: 6 additions & 8 deletions plugins/rangerguard/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ func (r *guardMux) Use(mwf ...http.HandlerFunc) {
func (gm guardMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
guardCtx, span := tracer.Start(r.Context(), "ranger.guard.ServeHTTP")
defer span.End()

r = r.WithContext(guardCtx)

// iterate over middle ware
for i := len(gm.middlewares) - 1; i >= 0; i-- {
fn := gm.middlewares[i]
Expand All @@ -88,20 +91,17 @@ func (gm guardMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user, authenticated, authnReq := gm.authenticate(r)
if !authenticated || user == nil {
log.Info().Str("component", "guard").Str("client", r.RemoteAddr).Str("uri", r.RequestURI).Msg("Unauthenticated")
ranger.HttpError(w, authnReq, AUTHENTICATION_DENIED_ERROR)
ranger.HttpError(span, w, authnReq, AUTHENTICATION_DENIED_ERROR)
span.End()
return
}

// tell hooks about the user
for i := range gm.hooks {
_, span := tracer.Start(guardCtx, "ranger.guard.ServeHTTP/hook/"+gm.hooks[i].Name())
newCtx, err := gm.hooks[i].Run(authnReq.Context(), user, r)
span.End()
if err != nil {
log.Error().Err(err).Str("hook", gm.hooks[i].Name()).Msg("could not authenticate because ranger guard hook returned an error")
ranger.HttpError(w, authnReq, AUTHENTICATION_DENIED_ERROR)
span.End()
ranger.HttpError(span, w, authnReq, AUTHENTICATION_DENIED_ERROR)
return
}
// assign new context
Expand All @@ -113,12 +113,10 @@ func (gm guardMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
authorized, authzReq := gm.authorize(authnReq, user)
if !authorized {
log.Info().Str("component", "guard").Str("client", r.RemoteAddr).Str("uri", r.RequestURI).Msg("Unauthorized")
ranger.HttpError(w, authzReq, PERMISSION_DENIED_ERROR)
span.End()
ranger.HttpError(span, w, authzReq, PERMISSION_DENIED_ERROR)
return
}

span.End()
gm.next.ServeHTTP(w, authzReq)
}

Expand Down
28 changes: 21 additions & 7 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import (

"go.mondoo.com/ranger-rpc/codes"
"go.mondoo.com/ranger-rpc/status"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
jsonpb "google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
Expand All @@ -30,6 +33,8 @@ var validContentTypes = map[string]struct{}{
ContentTypeJson: {},
}

var tracer = otel.Tracer("go.mondoo.com/mondoo/ranger-rpc")

// Method represents a RPC method and is used by protoc-gen-rangerrpc
type Method func(ctx context.Context, reqBytes *[]byte) (proto.Message, error)

Expand Down Expand Up @@ -57,44 +62,52 @@ type server struct {

// ServeHTTP is the main entry point for the http server.
func (s *server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithCancel(req.Context())
ctx, span := tracer.Start(req.Context(), "ranger.server.ServeHTTP", trace.WithAttributes(
attribute.String("mondoo.ranger.service", s.service.Name)))
defer span.End()

ctx, cancel := context.WithCancel(ctx)
defer cancel()

req = req.WithContext(ctx)

contentType := req.Header.Get("Content-Type")

// verify content type
err := verifyContentType(req, contentType)
if err != nil {
HttpError(w, req, err)
HttpError(span, w, req, err)
return
}

if !strings.HasPrefix(req.URL.Path, s.prefix) {
HttpError(w, req, status.Error(codes.NotFound, req.URL.Path+" is not available"))
HttpError(span, w, req, status.Error(codes.NotFound, req.URL.Path+" is not available"))
return
}

// extract the rpc method name and invoke the method
name := strings.TrimPrefix(req.URL.Path, s.prefix)

span.SetAttributes(attribute.String("mondoo.ranger.method", name))

method, ok := s.service.Methods[name]
if !ok {
err := status.Error(codes.NotFound, "method not defined")
HttpError(w, req, err)
HttpError(span, w, req, err)
return
}

rctx, rcancel, body, err := preProcessRequest(ctx, req)
if err != nil {
HttpError(w, req, err)
HttpError(span, w, req, err)
return
}
defer rcancel()

// invoke method and send the response
resp, err := method(rctx, &body)
if err != nil {
HttpError(w, req, err)
HttpError(span, w, req, err)
return
}
// check if the accept header is set, otherwise use the incoming content type
Expand Down Expand Up @@ -125,7 +138,8 @@ func preProcessRequest(ctx context.Context, req *http.Request) (context.Context,
func (s *server) sendResponse(w http.ResponseWriter, req *http.Request, resp proto.Message, contentType string) {
payload, contentType, err := convertProtoToPayload(resp, contentType)
if err != nil {
HttpError(w, req, status.Error(codes.Internal, "error encoding response"))
span := trace.SpanFromContext(req.Context())
HttpError(span, w, req, status.Error(codes.Internal, "error encoding response"))
return
}

Expand Down

0 comments on commit 24129d4

Please sign in to comment.