Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Base implementation of the structure of Eiffel-Goer. #1

Merged
merged 9 commits into from
Aug 31, 2021
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ all: test build start
gen:
go generate ./...
build: gen
go build -o bin/goer ./cmd/goer
go get github.com/ahmetb/govvv
t-persson marked this conversation as resolved.
Show resolved Hide resolved
govvv build -o bin/goer ./cmd/goer
clean:
rm ./bin/* || true
docker-compose --project-directory . -f deploy/$(DEPLOY)/docker-compose.yml rm || true
Expand Down
23 changes: 18 additions & 5 deletions cmd/goer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,40 @@ package main

import (
"context"
"os"

"github.com/eiffel-community/eiffel-goer/internal/config"
"github.com/eiffel-community/eiffel-goer/internal/logger"
"github.com/eiffel-community/eiffel-goer/pkg/application"
log "github.com/sirupsen/logrus"
)

// GitSummary contains "git describe" output and is automatically
// populated via linker options when building with govvv.
var GitSummary = "(unknown)"

// Start up the Goer application.
func main() {
cfg := config.Get()
ctx := context.Background()

app, err := application.Get(cfg)
if err := logger.Setup(cfg); err != nil {
log.Fatal(err)
}
log := log.WithFields(log.Fields{
"hostname": os.Getenv("HOSTNAME"),
t-persson marked this conversation as resolved.
Show resolved Hide resolved
"application": "eiffel-goer",
"version": GitSummary,
})
app, err := application.Get(cfg, log)
if err != nil {
logger.Error.Panic(err)
log.Panic(err)
}

app.LoadV1Alpha1Routes()

logger.Debug.Println("Starting up.")
log.Debug("Starting up.")
err = app.Start(ctx)
if err != nil {
logger.Error.Panic(err)
log.Panic(err)
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ require (
github.com/golang/mock v1.4.4
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/sirupsen/logrus v1.4.2
github.com/snowzach/rotatefilehook v0.0.0-20180327172521-2f64f265f58c
go.mongodb.org/mongo-driver v1.7.1
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
)
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down Expand Up @@ -45,6 +46,7 @@ github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0Lh
github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand All @@ -64,7 +66,10 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/snowzach/rotatefilehook v0.0.0-20180327172521-2f64f265f58c h1:iUEy7/LRto3JqR/GLXDTEFP+s+qIjWw4pM8yzMfXC9A=
github.com/snowzach/rotatefilehook v0.0.0-20180327172521-2f64f265f58c/go.mod h1:ZLVe3VfhAuMYLYWliGEydMBoRnfib8EFSqkBYu1ck9E=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down Expand Up @@ -105,6 +110,7 @@ golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2 h1:T5DasATyLQfmbTpfEXx/IOL9vfjzW6up+ZDkmHvIf2s=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
Expand All @@ -121,6 +127,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
19 changes: 19 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,15 @@ import (
type Config interface {
DBConnectionString() string
APIPort() string
LogLevel() string
LogFilePath() string
}

type Cfg struct {
connectionString string
apiPort string
logLevel string
logFilePath string
}

// Get parses input parameters to program and return a config with them set.
Expand All @@ -36,6 +40,8 @@ func Get() Config {

flag.StringVar(&conf.connectionString, "connectionstring", os.Getenv("CONNECTION_STRING"), "Database connection string.")
flag.StringVar(&conf.apiPort, "apiport", os.Getenv("API_PORT"), "API port.")
flag.StringVar(&conf.logLevel, "loglevel", os.Getenv("LOGLEVEL"), "Log level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL, PANIC).")
flag.StringVar(&conf.logFilePath, "logfilepath", os.Getenv("LOG_FILE_PATH"), "Path, including filename, for the log files to create.")

flag.Parse()
return conf
Expand All @@ -50,3 +56,16 @@ func (c *Cfg) DBConnectionString() string {
func (c *Cfg) APIPort() string {
return ":" + c.apiPort
}

// LogLevel returns the log level. Default is INFO.
func (c *Cfg) LogLevel() string {
if c.logLevel == "" {
c.logLevel = "INFO"
}
return c.logLevel
}

// LogFilePath returns the path to where log files should be stored, including filename.
func (c *Cfg) LogFilePath() string {
return c.logFilePath
}
48 changes: 35 additions & 13 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ import (
func TestGet(t *testing.T) {
port := "8080"
connectionString := "connection string"
logLevel := "DEBUG"
logFilePath := "path/to/a/file"
os.Setenv("CONNECTION_STRING", connectionString)
os.Setenv("API_PORT", port)
os.Setenv("LOGLEVEL", logLevel)
os.Setenv("LOG_FILE_PATH", logFilePath)

cfg, ok := Get().(*Cfg)
if !ok {
Expand All @@ -37,24 +41,42 @@ func TestGet(t *testing.T) {
if cfg.apiPort != port {
t.Error("api port not set to environment variable API_PORT")
}
}

// Test that DBConnectionString return the connectionString value from the Cfg struct
func TestDBConnectionString(t *testing.T) {
cfg := &Cfg{
connectionString: "connectionString",
if cfg.logLevel != logLevel {
t.Error("log level not set to environment variable LOGLEVEL")
}
if cfg.DBConnectionString() != "connectionString" {
t.Error("function does not return the connectionString from Cfg struct")
if cfg.logFilePath != logFilePath {
t.Error("log file path not set to environment variable LOG_FILE_PATH")
}
}

// Test that APIPort return the value from Cfg struct with a ':' at the start
func TestAPIPort(t *testing.T) {
type getter func() string

// Test that the getters in the Cfg struct return the values from the struct.
func TestGetters(t *testing.T) {
cfg := &Cfg{
apiPort: "8080",
connectionString: "something://db/test",
apiPort: "8080",
logLevel: "TRACE",
logFilePath: "a/file/path.json",
}
emptyCfg := &Cfg{}
tests := []struct {
name string
cfg *Cfg
function getter
value string
}{
{name: "DBConnectionString", cfg: cfg, function: cfg.DBConnectionString, value: cfg.connectionString},
{name: "APIPort", cfg: cfg, function: cfg.APIPort, value: ":" + cfg.apiPort},
{name: "LogLevel", cfg: cfg, function: cfg.LogLevel, value: cfg.logLevel},
{name: "LogLevelDefault", cfg: emptyCfg, function: emptyCfg.LogLevel, value: "INFO"},
{name: "LogFilePath", cfg: cfg, function: cfg.LogFilePath, value: cfg.logFilePath},
}
if cfg.APIPort() != ":8080" {
t.Error("function does not return the apiPort from Cfg struct")
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
if testCase.function() != testCase.value {
t.Errorf("function does not return %q from Cfg struct", testCase.value)
}
})
}
}
6 changes: 4 additions & 2 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"fmt"
"net/url"

log "github.com/sirupsen/logrus"

"github.com/eiffel-community/eiffel-goer/internal/database/drivers/mongodb"
"github.com/eiffel-community/eiffel-goer/pkg/schema"
)
Expand All @@ -34,14 +36,14 @@ type Database interface {
}

// Get a new Database.
func Get(connectionString string) (Database, error) {
func Get(connectionString string, logger *log.Entry) (Database, error) {
connectionURL, err := url.Parse(connectionString)
if err != nil {
return nil, err
}
switch connectionURL.Scheme {
case "mongodb":
return mongodb.Get(connectionURL)
return mongodb.Get(connectionURL, logger)
default:
return nil, fmt.Errorf("cannot find database for scheme %q", connectionURL.Scheme)
}
Expand Down
12 changes: 8 additions & 4 deletions internal/database/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,28 @@
// limitations under the License.
package database

import "testing"
import (
"testing"

log "github.com/sirupsen/logrus"
)

func TestGet(t *testing.T) {
_, err := Get("mongodb://db/test")
_, err := Get("mongodb://db/test", &log.Entry{})
if err != nil {
t.Error(err)
}
}

func TestGetUnknownScheme(t *testing.T) {
_, err := Get("unknown://db/test")
_, err := Get("unknown://db/test", &log.Entry{})
if err == nil {
t.Error("possible to get a database with unknown:// scheme")
}
}

func TestGetUnparsableScheme(t *testing.T) {
_, err := Get("://")
_, err := Get("://", &log.Entry{})
if err == nil {
t.Error("possible to get a database with an unparsable scheme")
}
Expand Down
5 changes: 4 additions & 1 deletion internal/database/drivers/mongodb/mongodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"net/url"
"strings"

log "github.com/sirupsen/logrus"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
Expand All @@ -37,10 +38,11 @@ import (
type MongoDB struct {
Client *mongo.Client
Database *mongo.Database
Logger *log.Entry
}

// Get creates a new database.Database interface against MongoDB.
func Get(connectionURL *url.URL) (*MongoDB, error) {
func Get(connectionURL *url.URL, logger *log.Entry) (*MongoDB, error) {
client, err := mongo.NewClient(options.Client().ApplyURI(connectionURL.String()))
if err != nil {
return nil, err
Expand All @@ -50,6 +52,7 @@ func Get(connectionURL *url.URL) (*MongoDB, error) {
return &MongoDB{
Client: client,
Database: client.Database(databaseName),
Logger: logger,
}, nil
}

Expand Down
40 changes: 30 additions & 10 deletions internal/logger/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,35 @@
package logger

import (
"log"
"os"
)
"github.com/sirupsen/logrus"
"github.com/snowzach/rotatefilehook"

// Add some logging handlers that can be used to append
// log level to the log message. Also redirect Error to Stderr.
var (
Debug = log.New(os.Stdout, "DEBUG\t", log.Ldate|log.Ltime)
Info = log.New(os.Stdout, "INFO\t", log.Ldate|log.Ltime)
Warning = log.New(os.Stdout, "WARNING\t", log.Ldate|log.Ltime)
Error = log.New(os.Stderr, "ERROR\t", log.Ldate|log.Ltime|log.Lshortfile)
"github.com/eiffel-community/eiffel-goer/internal/config"
)
magnusbaeck marked this conversation as resolved.
Show resolved Hide resolved

// Setup sets up logging to file with a JSON format and to stdout in text format.
func Setup(cfg config.Config) error {
logLevel, err := logrus.ParseLevel(cfg.LogLevel())
if err != nil {
return err
}
filePath := cfg.LogFilePath()
if filePath != "" {
// TODO: Make these parameters configurable.
rotateFileHook, err := rotatefilehook.NewRotateFileHook(rotatefilehook.RotateFileConfig{
Filename: filePath,
MaxSize: 10, // megabytes
MaxBackups: 3,
MaxAge: 0, // days
Level: logrus.DebugLevel,
Formatter: &logrus.JSONFormatter{},
})
if err != nil {
return err
}
logrus.AddHook(rotateFileHook)
}
logrus.SetLevel(logLevel)
logrus.SetFormatter(&logrus.TextFormatter{})
return nil
}
7 changes: 6 additions & 1 deletion pkg/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"context"

"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"

"github.com/eiffel-community/eiffel-goer/internal/config"
"github.com/eiffel-community/eiffel-goer/internal/database"
Expand All @@ -32,14 +33,16 @@ type Application struct {
Router *mux.Router
Server server.Server
V1Alpha1 *v1alpha1.V1Alpha1Application
Logger *log.Entry
}

// Get a new Goer application.
func Get(cfg config.Config) (*Application, error) {
func Get(cfg config.Config, logger *log.Entry) (*Application, error) {
application := &Application{
Config: cfg,
Router: mux.NewRouter(),
Server: server.Get(),
Logger: logger,
}
if cfg.DBConnectionString() != "" {
db, err := application.getDB()
Expand All @@ -55,6 +58,7 @@ func Get(cfg config.Config) (*Application, error) {
func (app *Application) getDB() (database.Database, error) {
db, err := database.Get(
app.Config.DBConnectionString(),
app.Logger,
)
if err != nil {
return nil, err
Expand All @@ -67,6 +71,7 @@ func (app *Application) LoadV1Alpha1Routes() {
app.V1Alpha1 = &v1alpha1.V1Alpha1Application{
Config: app.Config,
Database: app.Database,
Logger: app.Logger,
}
subrouter := app.Router.PathPrefix("/v1alpha1").Name("v1alpha1").Subrouter()
app.V1Alpha1.AddRoutes(subrouter)
Expand Down
Loading