fleet/server/errors/errors.go
Zachary Wasserman ba528a46f1 Build endpoints for osquery service methods (#245)
- Establish a pattern for host authentication
- Establish a pattern for error JSON
- Add transport and make endpoint functions
- Fix discovered bugs + update tests
2016-09-28 21:21:39 -07:00

126 lines
3.7 KiB
Go

package errors
import (
"net/http"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"gopkg.in/go-playground/validator.v8"
)
// Kolide's internal representation for errors. It can be used to wrap another
// error (stored in Err), and additionally contains fields for public
// (PublicMessage) and private (PrivateMessage) error messages as well as the
// HTTP status code (StatusCode) corresponding to the error. Extra holds extra
// information that will be inserted as top level key/value pairs in the error
// response.
type KolideError struct {
Err error
StatusCode int
PublicMessage string
PrivateMessage string
Extra map[string]interface{}
}
// Implementation of error interface
func (e *KolideError) Error() string {
return e.PublicMessage
}
// Create a new KolideError specifying the public and private messages. The
// status code will be set to 500.
func New(publicMessage, privateMessage string) *KolideError {
return &KolideError{
StatusCode: http.StatusInternalServerError,
PublicMessage: publicMessage,
PrivateMessage: privateMessage,
}
}
// Create a new KolideError specifying the HTTP status, and public and private
// messages.
func NewWithStatus(status int, publicMessage, privateMessage string) *KolideError {
return &KolideError{
StatusCode: status,
PublicMessage: publicMessage,
PrivateMessage: privateMessage,
}
}
// Create a new KolideError from an error type. The public message and status
// code should be specified, while the private message will be drawn from
// err.Error()
func NewFromError(err error, status int, publicMessage string) *KolideError {
return &KolideError{
Err: err,
StatusCode: status,
PublicMessage: publicMessage,
PrivateMessage: err.Error(),
}
}
// Wrap a DB error with the extra KolideError decorations
func DatabaseError(err error) *KolideError {
return NewFromError(err, http.StatusInternalServerError, "Database error")
}
// Wrap a server error with the extra KolideError decorations
func InternalServerError(err error) *KolideError {
return NewFromError(err, http.StatusInternalServerError, "Internal server error")
}
// The status code returned for validation errors. Inspired by the Github API.
const StatusUnprocessableEntity = 422
// Handle an error, printing debug information, writing to the HTTP response as
// appropriate for the dynamic error type.
func ReturnError(c *gin.Context, err error) {
baseReturnError(c, err, "message")
}
// osqueryd does not check the HTTP status code, and only looks for a
func ReturnOsqueryError(c *gin.Context, err error) {
baseReturnError(c, err, "error")
}
func baseReturnError(c *gin.Context, err error, messageKey string) {
switch typedErr := err.(type) {
case *KolideError:
errJSON := map[string]interface{}{}
for key, val := range typedErr.Extra {
errJSON[key] = val
}
errJSON[messageKey] = typedErr.PublicMessage
c.JSON(typedErr.StatusCode, errJSON)
logrus.WithError(typedErr.Err).Debug(typedErr.PrivateMessage)
case validator.ValidationErrors:
errors := make([](map[string]string), 0, len(typedErr))
for _, fieldErr := range typedErr {
m := make(map[string]string)
m["field"] = fieldErr.Name
m["code"] = "invalid"
m["message"] = fieldErr.Tag
errors = append(errors, m)
}
c.JSON(StatusUnprocessableEntity,
gin.H{messageKey: "Validation error",
"errors": errors,
})
logrus.WithError(typedErr).Debug("Validation error")
case gorm.Errors, *gorm.Errors:
c.JSON(http.StatusInternalServerError,
gin.H{messageKey: "Database error"})
logrus.WithError(typedErr).Debug(typedErr.Error())
default:
c.JSON(http.StatusInternalServerError,
gin.H{messageKey: "Unspecified error"})
logrus.WithError(typedErr).Debug("Unspecified error")
}
}