diff --git a/api_manager.go b/api_manager.go index 2e3be7d22..acb10b7d9 100644 --- a/api_manager.go +++ b/api_manager.go @@ -6,6 +6,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/etcinit/speedbump" "github.com/gin-gonic/gin" + "github.com/hellofresh/ginger-middleware/mongodb" "gopkg.in/redis.v3" ) @@ -13,11 +14,11 @@ import ( type APIManager struct { proxyRegister *ProxyRegister redisClient *redis.Client - accessor *DatabaseAccessor + accessor *mongodb.DatabaseAccessor } // NewAPIManager creates a new instance of the api manager -func NewAPIManager(router *gin.Engine, redisClient *redis.Client, accessor *DatabaseAccessor) *APIManager { +func NewAPIManager(router *gin.Engine, redisClient *redis.Client, accessor *mongodb.DatabaseAccessor) *APIManager { proxyRegister := &ProxyRegister{Engine: router} return &APIManager{proxyRegister, redisClient, accessor} } @@ -42,7 +43,7 @@ func (m *APIManager) LoadApps(apiSpecs []*APISpec) { skip = m.validateProxy(referenceSpec.Proxy) if false == referenceSpec.Active { - logger.Info("API is not active, skiping...") + log.Info("API is not active, skiping...") skip = false } diff --git a/config.go b/config.go deleted file mode 100644 index c76f74fc3..000000000 --- a/config.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -// Specification for basic configurations -type Specification struct { - DatabaseDSN string `envconfig:"DATABASE_DSN"` - Port int `envconfig:"PORT"` - Debug bool `envconfig:"DEBUG"` - StorageDSN string `envconfig:"REDIS_DSN"` -} diff --git a/config/specification.go b/config/specification.go new file mode 100644 index 000000000..b2adc5647 --- /dev/null +++ b/config/specification.go @@ -0,0 +1,22 @@ +package config + +import "github.com/kelseyhightower/envconfig" + +// Specification for basic configurations +type Specification struct { + DatabaseDSN string `envconfig:"DATABASE_DSN"` + Port int `envconfig:"PORT"` + Debug bool `envconfig:"DEBUG"` + StorageDSN string `envconfig:"REDIS_DSN"` +} + +//LoadEnv loads environment variables +func LoadEnv() (*Specification, error) { + var config Specification + err := envconfig.Process("", &config) + if err != nil { + return nil, err + } + + return &config, nil +} diff --git a/database_accessor.go b/database_accessor.go deleted file mode 100644 index c69326875..000000000 --- a/database_accessor.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - log "github.com/Sirupsen/logrus" - "github.com/gin-gonic/gin" - "gopkg.in/mgo.v2" -) - -// DatabaseAccessor represents a mongo database encapsulation -type DatabaseAccessor struct { - *mgo.Session -} - -// NewServer create a new mongo db server -func NewServer(dsn string) (*DatabaseAccessor, error) { - log.Debugf("Trying to connect to %s", dsn) - session, err := mgo.Dial(dsn) - - if err == nil { - log.Debug("Connected to session") - session.SetMode(mgo.Monotonic, true) - return &DatabaseAccessor{session}, nil - } - - return nil, err -} - -// Set a session to a context -func (da *DatabaseAccessor) Set(c *gin.Context, session *mgo.Session) { - db := da.DB("") - c.Set("db", db) - c.Set("mgoSession", session) -} diff --git a/errors.go b/errors.go deleted file mode 100644 index 443a3eb3e..000000000 --- a/errors.go +++ /dev/null @@ -1,6 +0,0 @@ -package main - -import "errors" - -// ErrInvalidID represents an invalid ID -var ErrInvalidID = errors.New("Please provide a valid ID") diff --git a/errors/error.go b/errors/error.go new file mode 100644 index 000000000..233da2438 --- /dev/null +++ b/errors/error.go @@ -0,0 +1,14 @@ +package errors + +type Error struct { + Code int + message string +} + +func New(code int, message string) *Error { + return &Error{code, message} +} + +func (e Error) Error() string { + return e.message +} diff --git a/glide.lock b/glide.lock index eb48e667e..213d78090 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: cc2e600a52a6abf804f3a99995cea8db4896037ae253016b14775bddb879dcdb -updated: 2016-10-14T14:36:33.72332452+02:00 +hash: fdcc58eaba6c355535535fd7afc38cba0b69a3607f943c4e3dc5711ef21f30a6 +updated: 2016-11-18T14:32:26.654263265+01:00 imports: - name: github.com/asaskevich/govalidator version: 7664702784775e51966f0885f5cd27435916517b @@ -8,57 +8,45 @@ imports: - name: github.com/facebookgo/clock version: 600d898af40aa09a7a93ecb9265d87b0504b6f03 - name: github.com/gin-gonic/gin - version: 9163ee543d3f1fab9b0ad8cdf6dc2a6ec2c07dbb + version: 2dae550eb5392006a4582ce9c90016a9b5a74e8b subpackages: - binding - render +- name: github.com/golang/protobuf + version: 2402d76f3d41f928c7902a765dfc872356dd3aad + subpackages: + - proto - name: github.com/gorilla/context version: 1ea25387ff6f684839d82767c1733ff4d4d15d0a +- name: github.com/hellofresh/ginger-middleware + version: ec61f5a5d55ea819c1f9890510d997e558cbdfaa + subpackages: + - mongodb + - nice - name: github.com/itsjamie/gin-cors version: 97b4a9da79331dfa2b6d35f4cdd1e50f5148859c - name: github.com/kelseyhightower/envconfig - version: ac12b1f15efba734211a556d8b125110dc538016 + version: 9aca109c9aec4633fced9717c4a09ecab3d33111 - name: github.com/manucorporat/sse - version: c574f6c50c8594f93d28b03a1bbd87b4a3899093 -- name: github.com/mattn/go-colorable - version: 043ae16291351db8465272edf465c9f388161627 -- name: github.com/mattn/go-isatty - version: 66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8 -- name: github.com/onsi/ginkgo - version: 462326b1628e124b23f42e87a8f2750e3c4e2d24 - subpackages: - - config - - internal/codelocation - - internal/containernode - - internal/failer - - internal/leafnodes - - internal/remote - - internal/spec - - internal/specrunner - - internal/suite - - internal/testingtproxy - - internal/writer - - reporters - - reporters/stenographer - - types + version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d - name: github.com/pborman/uuid version: c55201b036063326c5b1b89ccfe45a184973d073 - name: github.com/RangelReale/osin version: 4f10152eca12dd6663009dc2e9377f2fe4b18a78 - name: github.com/Sirupsen/logrus - version: 4b6ea7319e214d98c938f12692336f7ca9348d6b + version: d26492970760ca5d33129d2d799e34be5c4782eb - name: golang.org/x/net - version: 84ba27dd5b2d8135e9da1395277f2c9333a2ffda + version: f315505cf3349909cdf013ea56690da34e96a451 subpackages: - context - name: golang.org/x/sys version: a408501be4d17ee978c04a618e7a1b22af058c0e subpackages: - unix -- name: gopkg.in/bluesuncorp/validator.v5 - version: d5acf1dac43705f8bfbb71d878e290e2bed3950b - name: gopkg.in/bsm/ratelimit.v1 version: db14e161995a5177acef654cb0dd785e8ee8bc22 +- name: gopkg.in/go-playground/validator.v8 + version: c193cecd124b5cc722d7ee5538e945bdb3348435 - name: gopkg.in/mgo.v2 version: 3f83fa5005286a7fe593b055f0d7771a7dce4655 subpackages: @@ -73,14 +61,32 @@ imports: - internal/consistenthash - internal/hashtag - internal/pool +- name: gopkg.in/yaml.v2 + version: 31c299268d302dd0aa9a0dcf765a3d58971ac83f testImports: +- name: github.com/onsi/ginkgo + version: 462326b1628e124b23f42e87a8f2750e3c4e2d24 + subpackages: + - config + - internal/codelocation + - internal/containernode + - internal/failer + - internal/leafnodes + - internal/remote + - internal/spec + - internal/specrunner + - internal/suite + - internal/testingtproxy + - internal/writer + - reporters + - reporters/stenographer + - types - name: github.com/onsi/gomega - version: d59fa0ac68bb5dd932ee8d24eed631cdd519efc3 + version: a78ae492d53aad5a7a232d0d0462c14c400e3ee7 subpackages: - format - internal/assertion - internal/asyncassertion - - internal/oraclematcher - internal/testingtsupport - matchers - matchers/support/goraph/bipartitegraph @@ -88,5 +94,3 @@ testImports: - matchers/support/goraph/node - matchers/support/goraph/util - types -- name: gopkg.in/yaml.v2 - version: 31c299268d302dd0aa9a0dcf765a3d58971ac83f diff --git a/glide.yaml b/glide.yaml index 9197f83ca..16e081496 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,6 +1,9 @@ package: github.com/hellofresh/api-gateway +license: MIT +owners: +- name: Italo Lelis de Vietro + email: italolelis@gmail.com import: -- package: github.com/RangelReale/osin - package: github.com/Sirupsen/logrus version: ^0.10.0 - package: github.com/asaskevich/govalidator @@ -8,9 +11,7 @@ import: - package: github.com/etcinit/speedbump version: ^0.2.0 - package: github.com/gin-gonic/gin - version: 9163ee543d3f1fab9b0ad8cdf6dc2a6ec2c07dbb -- package: github.com/gorilla/context - version: ^1.1.0 + version: v1.0-rc.2 - package: github.com/kelseyhightower/envconfig version: ^1.1.0 - package: gopkg.in/mgo.v2 @@ -20,5 +21,7 @@ import: version: ^3.6.4 - package: github.com/itsjamie/gin-cors version: ^1.0.0 +- package: github.com/hellofresh/ginger-middleware +testImport: - package: github.com/onsi/ginkgo - version: ^1.2.0 + version: ^1.2.0 \ No newline at end of file diff --git a/handler_api_definition.go b/handler_api_definition.go index 83303e272..45f23cb65 100644 --- a/handler_api_definition.go +++ b/handler_api_definition.go @@ -3,8 +3,8 @@ package main import ( "net/http" - log "github.com/Sirupsen/logrus" "github.com/gin-gonic/gin" + "github.com/hellofresh/janus/errors" "gopkg.in/mgo.v2" ) @@ -16,11 +16,10 @@ type AppsAPI struct { func (u *AppsAPI) Get() gin.HandlerFunc { return func(c *gin.Context) { repo := u.getRepository(u.getDatabase(c)) - data, err := repo.FindAll() + data, err := repo.FindAll() if err != nil { - log.Errorf(err.Error()) - c.JSON(http.StatusInternalServerError, err.Error()) + panic(err.Error()) } c.JSON(http.StatusOK, data) @@ -35,14 +34,11 @@ func (u *AppsAPI) GetBy() gin.HandlerFunc { data, err := repo.FindByID(id) if data.ID == "" { - c.JSON(http.StatusNotFound, "Application not found") - return + panic(errors.New(http.StatusNotFound, "Application not found")) } if err != nil { - log.Errorf(err.Error()) - c.JSON(http.StatusInternalServerError, err.Error()) - return + panic(errors.New(http.StatusInternalServerError, err.Error())) } c.JSON(http.StatusOK, data) @@ -58,20 +54,16 @@ func (u *AppsAPI) PutBy() gin.HandlerFunc { repo := u.getRepository(u.getDatabase(c)) definition, err := repo.FindByID(id) if definition.ID == "" { - c.JSON(http.StatusNotFound, "Application not found") - return + panic(errors.New(http.StatusNotFound, "Application not found")) } if err != nil { - log.Errorf(err.Error()) - c.JSON(http.StatusInternalServerError, err.Error()) - return + panic(errors.New(http.StatusInternalServerError, err.Error())) } err = c.Bind(definition) if nil != err { - log.Errorf("Error when reading json: %s", err.Error()) - return + panic(errors.New(http.StatusInternalServerError, err.Error())) } repo.Add(definition) @@ -89,13 +81,12 @@ func (u *AppsAPI) Post() gin.HandlerFunc { err := c.Bind(definition) if nil != err { - log.Fatal("Error when reading json") + panic(errors.New(http.StatusInternalServerError, err.Error())) } err = repo.Add(definition) if nil != err { - c.JSON(http.StatusBadRequest, err) - return + panic(errors.New(http.StatusBadRequest, err.Error())) } u.apiManager.Load() @@ -111,8 +102,7 @@ func (u *AppsAPI) DeleteBy() gin.HandlerFunc { err := repo.Remove(id) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) - return + panic(errors.New(http.StatusInternalServerError, err.Error())) } u.apiManager.Load() @@ -124,7 +114,7 @@ func (u *AppsAPI) getDatabase(c *gin.Context) *mgo.Database { db, exists := c.Get("db") if false == exists { - log.Error("DB context was not set for this request") + panic(errors.New(http.StatusInternalServerError, "DB context was not set for this request")) } return db.(*mgo.Database) @@ -133,9 +123,8 @@ func (u *AppsAPI) getDatabase(c *gin.Context) *mgo.Database { // GetRepository gets the repository for the handlers func (u *AppsAPI) getRepository(db *mgo.Database) *MongoAPISpecRepository { repo, err := NewMongoAppRepository(db) - if err != nil { - log.Panic(err) + panic(errors.New(http.StatusInternalServerError, err.Error())) } return repo diff --git a/handler_error.go b/handler_error.go new file mode 100644 index 000000000..347514785 --- /dev/null +++ b/handler_error.go @@ -0,0 +1,24 @@ +package main + +import ( + "net/http" + + log "github.com/Sirupsen/logrus" + "github.com/gin-gonic/gin" + "github.com/hellofresh/janus/errors" +) + +// ErrInvalidID represents an invalid ID +var ErrInvalidID = errors.New(http.StatusBadRequest, "Please provide a valid ID") + +// Recovery handler for the apis +func recoveryHandler(c *gin.Context, err interface{}) { + switch err.(type) { + case *errors.Error: + internalErr := err.(*errors.Error) + log.Error(internalErr.Error()) + c.JSON(internalErr.Code, internalErr.Error()) + default: + c.JSON(http.StatusInternalServerError, err) + } +} diff --git a/main.go b/main.go index 93c26f99a..57d7fd9a5 100644 --- a/main.go +++ b/main.go @@ -6,38 +6,29 @@ import ( log "github.com/Sirupsen/logrus" "github.com/gin-gonic/gin" - "github.com/kelseyhightower/envconfig" + "github.com/hellofresh/ginger-middleware/mongodb" + "github.com/hellofresh/ginger-middleware/nice" + "github.com/hellofresh/janus/config" "gopkg.in/redis.v3" ) var APILoader = APIDefinitionLoader{} -var config Specification - -//loadConfigEnv loads environment variables -func loadConfigEnv() Specification { - err := envconfig.Process("", &config) - if err != nil { - log.Fatal(err.Error()) - } - - return config -} // initializeDatabase initializes a DB connection -func initializeDatabase() *DatabaseAccessor { - accessor, err := NewServer(config.DatabaseDSN) +func initializeDatabase(dsn string) *mongodb.DatabaseAccessor { + accessor, err := mongodb.InitDB(dsn) if err != nil { - log.Fatal(err) + log.Fatalf("Couldn't connect to the mongodb database: %s", err.Error()) } return accessor } // initializeRedis initializes a Redis connection -func initializeRedis() *redis.Client { - log.Infof("Trying to connect to redis instance: %s", config.StorageDSN) +func initializeRedis(dsn string) *redis.Client { + log.Debugf("Trying to connect to redis instance: %s", dsn) return redis.NewClient(&redis.Options{ - Addr: config.StorageDSN, + Addr: dsn, }) } @@ -57,9 +48,16 @@ func loadAPIEndpoints(router *gin.Engine, apiManager *APIManager) { } func main() { - loadConfigEnv() log.SetOutput(os.Stderr) - router := gin.Default() + + config, err := config.LoadEnv() + if nil != err { + log.Panic(err.Error()) + } + + router := gin.New() + router.Use(gin.Logger()) + router.Use(nice.Recovery(recoveryHandler)) if config.Debug { log.SetLevel(log.DebugLevel) @@ -69,13 +67,10 @@ func main() { gin.SetMode(gin.ReleaseMode) } - accessor := initializeDatabase() - defer accessor.Close() - - database := Database{accessor} - router.Use(database.Middleware()) + accessor := initializeDatabase(config.DatabaseDSN) + router.Use(mongodb.Middleware(accessor)) - redisStorage := initializeRedis() + redisStorage := initializeRedis(config.StorageDSN) defer redisStorage.Close() apiManager := NewAPIManager(router, redisStorage, accessor) diff --git a/middleware_database.go b/middleware_database.go deleted file mode 100644 index 9feb05042..000000000 --- a/middleware_database.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - log "github.com/Sirupsen/logrus" - "github.com/gin-gonic/gin" -) - -// Database represents a database connection -type Database struct { - dba *DatabaseAccessor -} - -//Middleware is the gin middleware for the database, this is different from the others since it's a -//database handler -func (m *Database) Middleware() gin.HandlerFunc { - return func(c *gin.Context) { - log.Debug("Starting Database middleware") - - reqSession := m.dba.Clone() - defer reqSession.Close() - m.dba.Set(c, reqSession) - } -}