Added support for hooks to customize behavior.

This commit is contained in:
Travis Reeder
2016-08-09 22:34:28 -07:00
parent 72a6d3aa5b
commit 8558d13f07
23 changed files with 324 additions and 42 deletions

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleAppCreate(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
wapp := &models.AppWrapper{}
@@ -32,12 +35,26 @@ func handleAppCreate(c *gin.Context) {
return
}
err = Api.FireBeforeAppUpdate(ctx, wapp.App)
if err != nil {
log.WithError(err).Errorln(models.ErrAppsCreate)
c.JSON(http.StatusInternalServerError, simpleError(err))
return
}
app, err := Api.Datastore.StoreApp(wapp.App)
if err != nil {
log.WithError(err).Debug(models.ErrAppsCreate)
log.WithError(err).Errorln(models.ErrAppsCreate)
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))
return
}
err = Api.FireAfterAppUpdate(ctx, wapp.App)
if err != nil {
log.WithError(err).Errorln(models.ErrAppsCreate)
c.JSON(http.StatusInternalServerError, simpleError(err))
return
}
c.JSON(http.StatusOK, app)
}

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleAppDelete(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
appName := c.Param("app")
err := Api.Datastore.RemoveApp(appName)

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleAppGet(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
appName := c.Param("app")
app, err := Api.Datastore.GetApp(appName)

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleAppList(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
filter := &models.AppFilter{}

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleAppUpdate(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
app := &models.App{}

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleRouteCreate(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
var wroute models.RouteWrapper

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleRouteDelete(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
appName := c.Param("app")
routeName := c.Param("route")

View File

@@ -3,13 +3,17 @@ package server
import (
"net/http"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleRouteGet(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
appName := c.Param("app")
routeName := c.Param("route")

View File

@@ -3,13 +3,17 @@ package server
import (
"net/http"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleRouteList(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
appName := c.Param("app")

View File

@@ -3,13 +3,16 @@ package server
import (
"net/http"
"github.com/Sirupsen/logrus"
"golang.org/x/net/context"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
func handleRouteUpdate(c *gin.Context) {
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
var wroute models.RouteWrapper

View File

@@ -7,25 +7,40 @@ import (
"strings"
"time"
"golang.org/x/net/context"
"encoding/json"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
"github.com/iron-io/functions/api/runner"
titancommon "github.com/iron-io/titan/common"
"github.com/satori/go.uuid"
)
func handleSpecial(c *gin.Context) {
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
err := Api.UseSpecialHandlers(c)
if err != nil {
log.WithError(err).Errorln("Error using special handler!")
// todo: what do we do here?
}
}
func handleRunner(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/v1") {
c.Status(http.StatusNotFound)
return
}
log := c.MustGet("log").(logrus.FieldLogger)
ctx := c.MustGet("ctx").(context.Context)
log := titancommon.Logger(ctx)
reqID := uuid.NewV5(uuid.Nil, fmt.Sprintf("%s%s%d", c.Request.RemoteAddr, c.Request.URL.Path, time.Now().Unix())).String()
c.Set("reqID", reqID)
c.Set("reqID", reqID) // todo: put this in the ctx instead of gin's
log = log.WithFields(logrus.Fields{"request_id": reqID})
@@ -54,8 +69,17 @@ func handleRunner(c *gin.Context) {
appName := c.Param("app")
if appName == "" {
host := strings.Split(c.Request.Host, ":")[0]
appName = strings.Split(host, ".")[0]
// check context, app can be added via special handlers
a, ok := c.Get("app")
if ok {
appName = a.(string)
}
}
// if still no appName, we gotta exit
if appName == "" {
log.WithError(err).Error(models.ErrAppsNotFound)
c.JSON(http.StatusBadRequest, simpleError(models.ErrAppsNotFound))
return
}
route := c.Param("route")

View File

@@ -3,17 +3,25 @@ package server
import (
"path"
"golang.org/x/net/context"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/ifaces"
"github.com/iron-io/functions/api/models"
titancommon "github.com/iron-io/titan/common"
)
// Would be nice to not have this is a global, but hard to pass things around to the
// handlers in Gin without it.
var Api *Server
type Server struct {
Router *gin.Engine
Config *models.Config
Datastore models.Datastore
Router *gin.Engine
Config *models.Config
Datastore models.Datastore
AppListeners []ifaces.AppListener
SpecialHandlers []ifaces.SpecialHandler
}
func New(ds models.Datastore, config *models.Config) *Server {
@@ -25,6 +33,49 @@ func New(ds models.Datastore, config *models.Config) *Server {
return Api
}
// AddAppListener adds a listener that will be notified on App changes.
func (s *Server) AddAppListener(listener ifaces.AppListener) {
s.AppListeners = append(s.AppListeners, listener)
}
func (s *Server) FireBeforeAppUpdate(ctx context.Context, app *models.App) error {
for _, l := range s.AppListeners {
err := l.BeforeAppUpdate(ctx, app)
if err != nil {
return err
}
}
return nil
}
func (s *Server) FireAfterAppUpdate(ctx context.Context, app *models.App) error {
for _, l := range s.AppListeners {
err := l.AfterAppUpdate(ctx, app)
if err != nil {
return err
}
}
return nil
}
func (s *Server) AddSpecialHandler(handler ifaces.SpecialHandler) {
s.SpecialHandlers = append(s.SpecialHandlers, handler)
}
func (s *Server) UseSpecialHandlers(ginC *gin.Context) error {
c := &SpecialHandlerContext{
server: s,
ginContext: ginC,
}
for _, l := range s.SpecialHandlers {
err := l.Handle(c)
if err != nil {
return err
}
}
return nil
}
func extractFields(c *gin.Context) logrus.Fields {
fields := logrus.Fields{"action": path.Base(c.HandlerName())}
for _, param := range c.Params {
@@ -33,9 +84,11 @@ func extractFields(c *gin.Context) logrus.Fields {
return fields
}
func (s *Server) Run() {
func (s *Server) Run(ctx context.Context) {
s.Router.Use(func(c *gin.Context) {
c.Set("log", logrus.WithFields(extractFields(c)))
ctx, _ := titancommon.LoggerWithFields(ctx, extractFields(c))
c.Set("ctx", ctx)
c.Next()
})
@@ -66,11 +119,12 @@ func bindHandlers(engine *gin.Engine) {
apps.PUT("/routes/*route", handleRouteUpdate)
apps.DELETE("/routes/*route", handleRouteDelete)
}
}
engine.Any("/r/:app/*route", handleRunner)
engine.NoRoute(handleRunner)
// This final route is used for extensions, see Server.Add
engine.NoRoute(handleSpecial)
}
func simpleError(err error) *models.Error {

View File

@@ -0,0 +1,28 @@
package server
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/iron-io/functions/api/models"
)
type SpecialHandlerContext struct {
server *Server
ginContext *gin.Context
}
func (c *SpecialHandlerContext) Request() *http.Request {
return c.ginContext.Request
}
func (c *SpecialHandlerContext) Datastore() models.Datastore {
return c.server.Datastore
}
func (c *SpecialHandlerContext) Set(key string, value interface{}) {
c.ginContext.Set(key, value)
}
func (c *SpecialHandlerContext) Get(key string) (value interface{}, exists bool) {
return c.ginContext.Get(key)
}

View File

@@ -6,6 +6,9 @@ import (
"github.com/gin-gonic/gin"
)
// Version of IronFunctions
var Version = "0.0.1"
func handleVersion(c *gin.Context) {
c.JSON(http.StatusNotImplemented, "Not Implemented")
c.JSON(http.StatusOK, gin.H{"version": Version})
}