diff --git a/CHANGELOG.md b/CHANGELOG.md index 72cec7d..cf1eb62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v0.0.4 [16/08/09] +- 16/08/09 chore(doc): update README (SFR) + chore(deploy): switch to smaller image alpine + refact(all): use anonymous attribute for router + refact(all): main action return errors + ## v0.0.3 [16/07/13] - 16/07/07 chore(vendor): update codegansta vendor to urfave (SFR) chore(docker): update Go version to 1.6 in Docker deployment diff --git a/Dockerfile b/Dockerfile index 2662d91..72cbc71 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,9 @@ # minimal linux distribution -FROM golang:1.6-wheezy +FROM golang:1.6-alpine # GO and PATH env variables already set in golang image +# to reduce download time +RUN apk add -U make # set the go path to import the source project WORKDIR $GOPATH/src/github.com/sebastienfr/handsongo @@ -11,7 +13,10 @@ ADD . $GOPATH/src/github.com/sebastienfr/handsongo # we install the required software, # we build handsongo program # we clean the system from all build dependencies -RUN make all && rm -rf $GOPATH/pkg && rm -rf $GOPATH/src +RUN make all && apk del make && \ + rm -rf /gopath/pkg && \ + rm -rf /gopath/src && \ + rm -rf /var/cache/apk/* # by default, the exposed ports are 8020 (HTTP) EXPOSE 8020 diff --git a/Makefile b/Makefile index 9e57932..7042c98 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ export GO15VENDOREXPERIMENT=1 # ----------------------------------------------------------------- # version -VERSION=0.0.3 +VERSION=0.0.4 BUILDDATE=$(shell date -u '+%s') BUILDHASH=$(shell git rev-parse --short HEAD) VERSION_FLAG=-ldflags "-X main.Version=$(VERSION) -X main.GitHash=$(BUILDHASH) -X main.BuildStmp=$(BUILDDATE)" @@ -130,7 +130,7 @@ dockerUp: dockerStop: docker-compose stop docker-compose kill - docker-compose rm --all + docker-compose rm dockerBuildUp: dockerStop dockerBuild dockerUp diff --git a/README.md b/README.md index 2f29529..cd40a42 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,30 @@ [![codebeat badge](https://codebeat.co/badges/4c43152a-e6f8-4781-b1b4-9f5c9c040c00)](https://codebeat.co/projects/github-com-sebastienfr-handsongo) [![Software License](http://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/sebastienfr/handsongo/blob/master/LICENSE) -This project is meant to help learning go. +This project is meant to help learning go. It provides a basic implementation of a REST microservice exposing a CRUD API. +Data are persisted in a MongoDB NoSQL database and the application is deployed in Docker. + +## Technical stack + +* [Docker](https://www.docker.com) +* [MongoDB NoSQL database](https://www.mongodb.com) +* [Go is the language](https://golang.org) +* [Gorilla Mux the URL router](https://github.com/gorilla/mux) +* [Gorilla Mux the request context manager](https://github.com/gorilla/context) +* [Urfave negroni Web HTTP middleware](https://github.com/urfave/negroni) +* [Urfave cli the command line client parser](https://github.com/urfave/cli) +* [Sirupsen the logger](https://github.com/Sirupsen/logrus) +* [The database driver](https://gopkg.in/mgo.v2) +* [Godep the dependency manager](https://github.com/tools/godep) +* [Golint the source linter](https://github.com/golang/lint/golint) + +## Architecture + +![main architecture](doc/img/main_architecture.png "Main architecture") + +![web architecture](doc/img/web_architecture.png "Web architecture") + +## Build ```shell make help diff --git a/doc/img/concurrency.png b/doc/img/concurrency.png new file mode 100644 index 0000000..84ddece Binary files /dev/null and b/doc/img/concurrency.png differ diff --git a/doc/img/gopath_vendor.png b/doc/img/gopath_vendor.png new file mode 100644 index 0000000..2dd2087 Binary files /dev/null and b/doc/img/gopath_vendor.png differ diff --git a/doc/img/main_architecture.png b/doc/img/main_architecture.png new file mode 100644 index 0000000..d7793a1 Binary files /dev/null and b/doc/img/main_architecture.png differ diff --git a/doc/img/web_architecture.png b/doc/img/web_architecture.png new file mode 100644 index 0000000..7ab475c Binary files /dev/null and b/doc/img/web_architecture.png differ diff --git a/handsongo.go b/handsongo.go index 7fde8a6..8b4c9c1 100644 --- a/handsongo.go +++ b/handsongo.go @@ -84,8 +84,8 @@ func main() { } // main action - // sub action are possible also - app.Action = func(c *cli.Context) { + // sub action are also possible + app.Action = func(c *cli.Context) error { // print header fmt.Println(string(header)) @@ -96,13 +96,13 @@ func main() { logFormat = c.String("logf") statisticsDuration = c.Duration("statd") - fmt.Printf("* --------------------------------------------------- *\n") + fmt.Print("* --------------------------------------------------- *\n") fmt.Printf("| port : %d\n", port) fmt.Printf("| db : %s\n", db) fmt.Printf("| logger level : %s\n", logLevel) fmt.Printf("| logger format : %s\n", logFormat) fmt.Printf("| statistic duration(s) : %0.f\n", statisticsDuration.Seconds()) - fmt.Printf("* --------------------------------------------------- *\n") + fmt.Print("* --------------------------------------------------- *\n") // init log options from command line params err := utils.InitLog(logLevel, logFormat) @@ -110,10 +110,16 @@ func main() { logger.Warn("error setting log level, using debug as default") } - webServer := web.BuildWebServer(db, dao.DAOMongo, statisticsDuration) + webServer, err := web.BuildWebServer(db, dao.DAOMongo, statisticsDuration) + + if err != nil { + return err + } // serve webServer.Run(":" + strconv.Itoa(port)) + + return nil } // run the app diff --git a/web/router.go b/web/router.go index 49c0174..f12e26b 100644 --- a/web/router.go +++ b/web/router.go @@ -7,6 +7,11 @@ import ( "net/http" ) +// Router is the struct use for routing +type Router struct { + *mux.Router +} + // Route is a structure of Route type Route struct { Name string @@ -15,28 +20,21 @@ type Route struct { HandlerFunc http.HandlerFunc } -// Router is the struct use for routing -type Router struct { - Mux *mux.Router -} - // NewRouter creates a new router instance func NewRouter(handler *SpiritHandler) *Router { // new router - router := Router{ - Mux: mux.NewRouter(), - } + router := Router{mux.NewRouter()} // default JSON not found handler - router.Mux.NotFoundHandler = utils.NotFoundHandler() + router.NotFoundHandler = utils.NotFoundHandler() // no strict slash - router.Mux.StrictSlash(false) + router.StrictSlash(false) // add routes of handler for _, route := range handler.Routes { logger.WithField("route", route).Debug("adding route to mux") - router.Mux. + router. Methods(route.Method). Path(handler.Prefix + route.Pattern). Name(route.Name). diff --git a/web/web-server.go b/web/web-server.go index 70d2d2a..8b9f05c 100644 --- a/web/web-server.go +++ b/web/web-server.go @@ -9,12 +9,13 @@ import ( ) // BuildWebServer constructs a new web server with the right DAO and spirits handler -func BuildWebServer(db string, daoType int, statisticsDuration time.Duration) *negroni.Negroni { +func BuildWebServer(db string, daoType int, statisticsDuration time.Duration) (*negroni.Negroni, error) { // spirit dao dao, err := dao.GetSpiritDAO(db, daoType) if err != nil { logger.WithField("error", err).WithField("connection string", db).Fatal("unable to connect to mongo db") + return nil, err } // web server @@ -22,6 +23,7 @@ func BuildWebServer(db string, daoType int, statisticsDuration time.Duration) *n // new handler handler := NewSpiritHandler(dao) + // new router router := NewRouter(handler) @@ -39,7 +41,7 @@ func BuildWebServer(db string, daoType int, statisticsDuration time.Duration) *n // add as many middleware as you like // handler goes last - n.UseHandler(router.Mux) + n.UseHandler(router) - return n + return n, nil } diff --git a/web/web_test.go b/web/web_test.go index c3a1e3b..0e9b071 100644 --- a/web/web_test.go +++ b/web/web_test.go @@ -46,10 +46,17 @@ func TestSpiritHandlerGet(t *testing.T) { func TestSpiritHandlerGetServer(t *testing.T) { - ts := httptest.NewServer(BuildWebServer("", dao.DAOMock, 250*time.Millisecond)) + srv, err := BuildWebServer("", dao.DAOMock, 250*time.Millisecond) + + if err != nil { + t.Error(err) + } + + ts := httptest.NewServer(srv) defer ts.Close() res, err := http.Get(ts.URL + "/spirits") + if err != nil { t.Error(err) }