upd(all): restructure cmd, flags, startups

This commit is contained in:
Tit Petric
2018-08-23 11:15:23 +02:00
parent 7c62652761
commit 16cc7a11f3
20 changed files with 316 additions and 192 deletions
+1 -1
View File
@@ -23,7 +23,7 @@ func (c configuration) validate() error {
}
// Flags should be called from main to register flags
func Flags() {
func Flags(_ ...string) {
flag.StringVar(&config.jwtSecret, "auth-jwt-secret", "", "JWT Secret")
flag.Int64Var(&config.jwtExpiry, "auth-jwt-expiry", 3600, "JWT Expiration in minutes")
}
+2 -10
View File
@@ -6,24 +6,16 @@ import (
)
type configuration struct {
httpAddr string
dbDSN string
monitorInterval int
}
func flags(prefix string, mountFlags ...func()) configuration {
func flags(prefix string, mountFlags ...func(...string)) configuration {
var config configuration
p := func(s string) string {
return prefix + "-" + s
}
flag.StringVar(&config.httpAddr, p("http-addr"), ":3000", "Listen address for HTTP server")
flag.StringVar(&config.dbDSN, p("db-dsn"), "crust:crust@tcp(db1:3306)/crust?collation=utf8mb4_general_ci", "DSN for database connection")
flag.IntVar(&config.monitorInterval, "monitor-interval", 300, "Monitor interval (seconds, 0 = disable)")
for _, mount := range mountFlags {
mount()
mount(prefix)
}
flag.Parse()
+9 -42
View File
@@ -2,58 +2,25 @@ package main
import (
"log"
"net"
"os"
"net/http"
"github.com/go-chi/chi"
"github.com/crusttech/crust/crm"
"github.com/crusttech/crust/auth"
"github.com/crusttech/crust/crm/rest"
"github.com/crusttech/crust/rbac"
"github.com/titpetric/factory"
)
func handleError(err error, message string) {
if message == "" {
message = "Error making API call"
}
if err != nil {
log.Fatalf(message+": %v", err.Error())
}
}
func main() {
config := flags("crm", rbac.Flags, auth.Flags)
config := flags("crm", crm.Flags, rbac.Flags, auth.Flags)
// log to stdout not stderr
log.SetOutput(os.Stdout)
log.SetFlags(log.LstdFlags | log.Lshortfile)
go NewMonitor(config.monitorInterval)
// set up database connection
factory.Database.Add("default", config.dbDSN)
db, err := factory.Database.Get()
handleError(err, "Can't connect to database")
db.Profiler = &factory.Database.ProfilerStdout
// listen socket for http server
log.Println("Starting http server on address " + config.httpAddr)
listener, err := net.Listen("tcp", config.httpAddr)
handleError(err, "Can't listen on addr "+config.httpAddr)
// route options
routeOptions, err := RouteOptions{}.New()
handleError(err, "Error creating RouteOptions object")
r := chi.NewRouter()
// JWT Auth
jwtAuth, err := auth.JWT()
handleError(err, "Error creating JWT Auth object")
r.Use(jwtAuth.Verifier(), jwtAuth.Authenticator())
// mount routes
MountRoutes(r, routeOptions, rest.MountRoutes(jwtAuth))
http.Serve(listener, r)
if err := crm.Init(); err != nil {
log.Fatalf("Error initializing crm: %+v", err)
}
if err := crm.Start(); err != nil {
log.Fatalf("Error starting/running crm: %+v", err)
}
}
-16
View File
@@ -1,16 +0,0 @@
package main
type RouteOptions struct {
enableLogging bool
}
func (RouteOptions) New() (*RouteOptions, error) {
opts := &RouteOptions{}
opts.enableLogging = true
return opts, nil
}
func (o *RouteOptions) EnableLogging(enable bool) *RouteOptions {
o.enableLogging = enable
return o
}
+2 -10
View File
@@ -6,24 +6,16 @@ import (
)
type configuration struct {
httpAddr string
dbDSN string
monitorInterval int
}
func flags(prefix string, mountFlags ...func()) configuration {
func flags(prefix string, mountFlags ...func(...string)) configuration {
var config configuration
p := func(s string) string {
return prefix + "-" + s
}
flag.StringVar(&config.httpAddr, p("http-addr"), ":3000", "Listen address for HTTP server")
flag.StringVar(&config.dbDSN, p("db-dsn"), "crust:crust@tcp(db1:3306)/crust?collation=utf8mb4_general_ci", "DSN for database connection")
flag.IntVar(&config.monitorInterval, "monitor-interval", 300, "Monitor interval (seconds, 0 = disable)")
for _, mount := range mountFlags {
mount()
mount(prefix)
}
flag.Parse()
+10 -37
View File
@@ -1,17 +1,12 @@
package main
import (
"github.com/SentimensRG/sigctx"
"github.com/crusttech/crust/auth"
"github.com/crusttech/crust/rbac"
"github.com/crusttech/crust/sam/rest"
"github.com/crusttech/crust/sam/websocket"
"github.com/go-chi/chi"
"github.com/titpetric/factory"
"github.com/crusttech/crust/sam"
"log"
"net"
"net/http"
"os"
)
@@ -25,39 +20,17 @@ func handleError(err error, message string) {
}
func main() {
var ctx = sigctx.New()
config := flags("sam", auth.Flags, rbac.Flags, websocket.Flags)
config := flags("sam", sam.Flags, auth.Flags, rbac.Flags)
// log to stdout not stderr
log.SetOutput(os.Stdout)
log.SetFlags(log.LstdFlags | log.Lshortfile)
go NewMonitor(config.monitorInterval)
// set up database connection
factory.Database.Add("default", config.dbDSN)
db, err := factory.Database.Get()
handleError(err, "Can't connect to database")
db.Profiler = &factory.Database.ProfilerStdout
// listen socket for http server
log.Println("Starting http server on address " + config.httpAddr)
listener, err := net.Listen("tcp", config.httpAddr)
handleError(err, "Can't listen on addr "+config.httpAddr)
// route options
routeOptions, err := RouteOptions{}.New()
handleError(err, "Error creating RouteOptions object")
r := chi.NewRouter()
// JWT Auth
jwtAuth, err := auth.JWT()
handleError(err, "Error creating JWT Auth object")
r.Use(jwtAuth.Verifier(), jwtAuth.Authenticator())
// mount REST & WS routes
MountRoutes(r, routeOptions, rest.MountRoutes(jwtAuth), websocket.MountRoutes(ctx))
go http.Serve(listener, r)
<-ctx.Done()
if err := sam.Init(); err != nil {
log.Fatalf("Error initializing sam: %+v", err)
}
if err := sam.Start(); err != nil {
log.Fatalf("Error starting/running sam: %+v", err)
}
}
-16
View File
@@ -1,16 +0,0 @@
package main
type RouteOptions struct {
enableLogging bool
}
func (RouteOptions) New() (*RouteOptions, error) {
opts := &RouteOptions{}
opts.enableLogging = true
return opts, nil
}
func (o *RouteOptions) EnableLogging(enable bool) *RouteOptions {
o.enableLogging = enable
return o
}
+51
View File
@@ -0,0 +1,51 @@
package crm
import (
"github.com/namsral/flag"
"github.com/pkg/errors"
)
type (
configuration struct {
http struct {
addr string
logging bool
}
db struct {
dsn string
}
}
)
var config *configuration
func (c *configuration) Validate() error {
if c == nil {
return errors.New("CRM config is not initialized, need to call Flags()")
}
if c.http.addr == "" {
return errors.New("No HTTP Addr is set, can't listen for HTTP")
}
if c.db.dsn == "" {
return errors.New("No DB DSN is set, can't connect to database")
}
return nil
}
func Flags(prefix ...string) {
if config != nil {
return
}
if len(prefix) == 0 {
panic("crm.Flags() needs prefix on first call")
}
config := new(configuration)
p := func(s string) string {
return prefix[0] + "-" + s
}
flag.StringVar(&config.http.addr, p("http-addr"), ":3000", "Listen address for HTTP server")
flag.BoolVar(&config.http.logging, p("http-log"), true, "Enable/disable HTTP request log")
flag.StringVar(&config.db.dsn, p("db-dsn"), "crust:crust@tcp(db1:3306)/crust?collation=utf8mb4_general_ci", "DSN for database connection")
}
+3 -3
View File
@@ -1,4 +1,4 @@
package main
package crm
import (
"fmt"
@@ -11,7 +11,7 @@ import (
)
// MountRoutes will register API routes
func MountRoutes(r chi.Router, opts *RouteOptions, mountRoutes ...func(r chi.Router)) {
func MountRoutes(r chi.Router, opts *configuration, mountRoutes ...func(r chi.Router)) {
// CORS for local development...
cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
@@ -22,7 +22,7 @@ func MountRoutes(r chi.Router, opts *RouteOptions, mountRoutes ...func(r chi.Rou
})
r.Use(cors.Handler)
if opts.enableLogging {
if opts.http.logging {
r.Use(middleware.Logger)
}
+60
View File
@@ -0,0 +1,60 @@
package crm
import (
"fmt"
"log"
"net"
"net/http"
"github.com/SentimensRG/sigctx"
"github.com/go-chi/chi"
"github.com/pkg/errors"
"github.com/crusttech/crust/auth"
"github.com/crusttech/crust/crm/rest"
"github.com/titpetric/factory"
)
func Init() error {
// validate configuration
if err := config.Validate(); err != nil {
return err
}
// start/configure database connection
factory.Database.Add("default", config.db.dsn)
db, err := factory.Database.Get()
if err != nil {
return err
}
db.Profiler = &factory.Database.ProfilerStdout
return nil
}
func Start() error {
var ctx = sigctx.New()
log.Println("Starting http server on address " + config.http.addr)
listener, err := net.Listen("tcp", config.http.addr)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Can't listen on addr %s", config.http.addr))
}
// JWT Auth
jwtAuth, err := auth.JWT()
if err != nil {
return errors.Wrap(err, "Error creating JWT Auth object")
}
r := chi.NewRouter()
r.Use(jwtAuth.Verifier(), jwtAuth.Authenticator())
// mount routes
MountRoutes(r, config, rest.MountRoutes(jwtAuth))
go http.Serve(listener, r)
<-ctx.Done()
return nil
}
+1 -1
View File
@@ -30,7 +30,7 @@ func (c configuration) validate() error {
}
// Flags should be called from main to register flags
func Flags() {
func Flags(_ ...string) {
flag.StringVar(&config.auth, "rbac-auth", "username:password", "Credentials to use for RBAC queries")
flag.StringVar(&config.tenant, "rbac-tenant", "", "Tenant ID")
flag.StringVar(&config.baseURL, "rbac-base-url", "", "RBAC Base URL")
+58
View File
@@ -0,0 +1,58 @@
package sam
import (
"github.com/namsral/flag"
"github.com/pkg/errors"
"github.com/crusttech/crust/sam/websocket"
)
type (
configuration struct {
http struct {
addr string
logging bool
}
websocket websocket.Configuration
db struct {
dsn string
}
}
)
var config *configuration
func (c *configuration) Validate() error {
if c == nil {
return errors.New("SAM config is not initialized, need to call Flags()")
}
if c.http.addr == "" {
return errors.New("No HTTP Addr is set, can't listen for HTTP")
}
if c.db.dsn == "" {
return errors.New("No DB DSN is set, can't connect to database")
}
if err := c.websocket.Validate(); err != nil {
return err
}
return nil
}
func Flags(prefix ...string) {
if config != nil {
return
}
if len(prefix) == 0 {
panic("sam.Flags() needs prefix on first call")
}
config := new(configuration)
(&config.websocket).Init()
p := func(s string) string {
return prefix[0] + "-" + s
}
flag.StringVar(&config.http.addr, p("http-addr"), ":3000", "Listen address for HTTP server")
flag.BoolVar(&config.http.logging, p("http-log"), true, "Enable/disable HTTP request log")
flag.StringVar(&config.db.dsn, p("db-dsn"), "crust:crust@tcp(db1:3306)/crust?collation=utf8mb4_general_ci", "DSN for database connection")
}
+3 -3
View File
@@ -1,4 +1,4 @@
package main
package sam
import (
"fmt"
@@ -11,7 +11,7 @@ import (
)
// MountRoutes will register API routes
func MountRoutes(r chi.Router, opts *RouteOptions, mountRoutes ...func(r chi.Router)) {
func MountRoutes(r chi.Router, opts *configuration, mountRoutes ...func(r chi.Router)) {
// CORS for local development...
cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
@@ -22,7 +22,7 @@ func MountRoutes(r chi.Router, opts *RouteOptions, mountRoutes ...func(r chi.Rou
})
r.Use(cors.Handler)
if opts.enableLogging {
if opts.http.logging {
r.Use(middleware.Logger)
}
+61
View File
@@ -0,0 +1,61 @@
package sam
import (
"fmt"
"log"
"net"
"net/http"
"github.com/SentimensRG/sigctx"
"github.com/go-chi/chi"
"github.com/pkg/errors"
"github.com/crusttech/crust/auth"
"github.com/crusttech/crust/sam/rest"
"github.com/crusttech/crust/sam/websocket"
"github.com/titpetric/factory"
)
func Init() error {
// validate configuration
if err := config.Validate(); err != nil {
return err
}
// start/configure database connection
factory.Database.Add("default", config.db.dsn)
db, err := factory.Database.Get()
if err != nil {
return err
}
db.Profiler = &factory.Database.ProfilerStdout
return nil
}
func Start() error {
var ctx = sigctx.New()
log.Println("Starting http server on address " + config.http.addr)
listener, err := net.Listen("tcp", config.http.addr)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Can't listen on addr %s", config.http.addr))
}
// JWT Auth
jwtAuth, err := auth.JWT()
if err != nil {
return errors.Wrap(err, "Error creating JWT Auth object")
}
r := chi.NewRouter()
r.Use(jwtAuth.Verifier(), jwtAuth.Authenticator())
// mount routes
MountRoutes(r, config, rest.MountRoutes(jwtAuth), websocket.MountRoutes(ctx, config.websocket))
go http.Serve(listener, r)
<-ctx.Done()
return nil
}
+43
View File
@@ -0,0 +1,43 @@
package websocket
import (
"github.com/namsral/flag"
"github.com/pkg/errors"
"time"
)
type (
Configuration struct {
writeTimeout time.Duration
pingTimeout time.Duration
pingPeriod time.Duration
pubSubMode string
pubSubRedis string
pubSubInterval time.Duration
}
)
// Validate returns error if there is an issue with the config
func (c *Configuration) Validate() error {
switch c.pubSubMode {
case "redis", "poll":
default:
return errors.Errorf("Unknown pubSubMode: %s", c.pubSubMode)
}
if c.pubSubMode == "redis" && c.pubSubRedis == "" {
return errors.New("No host defined for mode=redis, pubSubRedis is empty")
}
return nil
}
// Init binds flags to websocket configuration structure
func (c *Configuration) Init() {
c.writeTimeout = 15 * time.Second
c.pingTimeout = 120 * time.Second
c.pingPeriod = (c.pingTimeout * 10) / 9
flag.StringVar(&c.pubSubMode, "pubsub", "poll", "Pubsub mode (poll, redis)")
flag.StringVar(&c.pubSubRedis, "pubsub-redis", "", "Redis Pub/Sub hostname")
flag.DurationVar(&c.pubSubInterval, "pubsub-poll-interval", 3*time.Second, "Pub/Sub polling interval (3s, 12m, 3h...)")
}
+1 -1
View File
@@ -57,7 +57,7 @@ func (eq *eventQueue) store(ctx context.Context, qp eventQueuePusher) {
}()
}
func (eq *eventQueue) feedSessions(ctx context.Context, qp eventQueuePuller, store eventQueueWalker) error {
func (eq *eventQueue) feedSessions(ctx context.Context, config Configuration, qp eventQueuePuller, store eventQueueWalker) error {
newMessageEvent := make(chan struct{}, eventQueueBacklog)
done := make(chan error, 1)
-44
View File
@@ -1,44 +0,0 @@
package websocket
import (
"github.com/namsral/flag"
"github.com/pkg/errors"
"time"
)
type (
configuration struct {
writeTimeout time.Duration
pingTimeout time.Duration
pingPeriod time.Duration
pubSubMode string
pubSubRedis string
pubSubInterval time.Duration
}
)
var config configuration
func (c configuration) validate() error {
switch c.pubSubMode {
case "redis", "poll":
default:
return errors.Errorf("Unknown pubSubMode: %s", c.pubSubMode)
}
if c.pubSubMode == "redis" && c.pubSubRedis == "" {
return errors.New("No host defined for mode=redis, pubSubRedis is empty")
}
return nil
}
// Flags should be called from main to register flags
func Flags() {
config.writeTimeout = 15 * time.Second
config.pingTimeout = 120 * time.Second
config.pingPeriod = (config.pingTimeout * 10) / 9
flag.StringVar(&config.pubSubMode, "pubsub", "poll", "Pubsub mode (poll, redis)")
flag.StringVar(&config.pubSubRedis, "pubsub-redis", "", "Redis Pub/Sub hostname")
flag.DurationVar(&config.pubSubInterval, "pubsub-poll-interval", 3*time.Second, "Pub/Sub polling interval (3s, 12m, 3h...)")
}
+3 -3
View File
@@ -7,7 +7,7 @@ import (
"github.com/go-chi/chi"
)
func MountRoutes(ctx context.Context) func(chi.Router) {
func MountRoutes(ctx context.Context, config Configuration) func(chi.Router) {
return func(r chi.Router) {
var (
// @todo move this 1 level up & join with rest init functions
@@ -16,10 +16,10 @@ func MountRoutes(ctx context.Context) func(chi.Router) {
repo := repository.New()
go eq.feedSessions(ctx, repo, store)
go eq.feedSessions(ctx, config, repo, store)
eq.store(ctx, repo)
websocket := Websocket{}.New(svcUser)
websocket := Websocket{}.New(svcUser, config)
r.Group(func(r chi.Router) {
r.Route("/websocket", func(r chi.Router) {
r.Get("/", websocket.Open)
+2 -2
View File
@@ -27,13 +27,13 @@ type (
remoteAddr string
config configuration
config Configuration
user *types.User
}
)
func (Session) New(ctx context.Context, conn *websocket.Conn) *Session {
func (Session) New(ctx context.Context, config Configuration, conn *websocket.Conn) *Session {
return &Session{
conn: conn,
ctx: ctx,
+6 -3
View File
@@ -17,6 +17,7 @@ type (
svc struct {
userFinder wsUserFinder
}
config Configuration
}
wsUserFinder interface {
@@ -24,8 +25,10 @@ type (
}
)
func (Websocket) New(svcUser wsUserFinder) *Websocket {
ws := &Websocket{}
func (Websocket) New(svcUser wsUserFinder, config Configuration) *Websocket {
ws := &Websocket{
config: config,
}
ws.svc.userFinder = svcUser
return ws
}
@@ -65,7 +68,7 @@ func (ws Websocket) Open(w http.ResponseWriter, r *http.Request) {
return
}
session := store.Save((&Session{}).New(ctx, conn))
session := store.Save((&Session{}).New(ctx, ws.config, conn))
session.user = user
if err := session.Handle(); err != nil {