changeset 220:11d1a488b08f

Depend command line parsing on Viper/Cobra. Configuration should be persistent now.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 24 Jul 2018 17:45:17 +0200
parents ddc78991edbc
children 696c19abe869
files 3rdpartylibs.sh auth/connection.go auth/pool.go cmd/gemma/main.go cmd/gemma/root.go config/config.go
diffstat 6 files changed, 143 insertions(+), 106 deletions(-) [+]
line wrap: on
line diff
--- a/3rdpartylibs.sh	Tue Jul 24 16:19:05 2018 +0200
+++ b/3rdpartylibs.sh	Tue Jul 24 17:45:17 2018 +0200
@@ -1,3 +1,6 @@
 #!/bin/sh
 go get -u -v github.com/jackc/pgx
 go get -u -v github.com/coreos/bbolt/...
+go get -u -v github.com/mitchellh/go-homedir
+go get -u -v github.com/spf13/cobra
+go get -u -v github.com/spf13/viper
--- a/auth/connection.go	Tue Jul 24 16:19:05 2018 +0200
+++ b/auth/connection.go	Tue Jul 24 17:45:17 2018 +0200
@@ -7,20 +7,10 @@
 	"log"
 	"sync"
 	"time"
-
-	"gemma.intevation.de/gemma/config"
 )
 
 var ErrNoSuchToken = errors.New("No such token")
 
-var ConnPool = func() *ConnectionPool {
-	cp, err := NewConnectionPool(config.Config.SessionStore)
-	if err != nil {
-		log.Panicf("Error with session store: %v\n", err)
-	}
-	return cp
-}()
-
 const (
 	maxOpen   = 16
 	maxDBIdle = time.Minute * 5
--- a/auth/pool.go	Tue Jul 24 16:19:05 2018 +0200
+++ b/auth/pool.go	Tue Jul 24 17:45:17 2018 +0200
@@ -9,6 +9,9 @@
 	bolt "github.com/coreos/bbolt"
 )
 
+// ConnPool is the global connection pool.
+var ConnPool *ConnectionPool
+
 type ConnectionPool struct {
 	storage *bolt.DB
 	conns   map[string]*Connection
--- a/cmd/gemma/main.go	Tue Jul 24 16:19:05 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-	"log"
-	"net/http"
-	"os"
-	"os/signal"
-	"path/filepath"
-	"syscall"
-
-	"gemma.intevation.de/gemma/auth"
-)
-
-func main() {
-	port := flag.Int("port", 8000, "port to listen at.")
-	host := flag.String("host", "localhost", "host to listen at.")
-	flag.Parse()
-	p, _ := filepath.Abs("./web")
-	mux := http.NewServeMux()
-	mux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(p))))
-	mux.HandleFunc("/api/token", token)
-	mux.Handle("/api/logout", auth.SessionMiddleware(http.HandlerFunc(token)))
-	mux.Handle("/api/renew", auth.SessionMiddleware(http.HandlerFunc(renew)))
-	mux.Handle("/api/create_user",
-		auth.SessionMiddleware(
-			auth.SessionChecker(http.HandlerFunc(createUser), auth.HasRole("sys_admin"))))
-
-	addr := fmt.Sprintf("%s:%d", *host, *port)
-
-	server := http.Server{Addr: addr, Handler: mux}
-
-	done := make(chan error)
-
-	go func() {
-		defer close(done)
-		done <- server.ListenAndServe()
-	}()
-
-	sigChan := make(chan os.Signal)
-	signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
-
-	select {
-	case err := <-done:
-		if err != nil && err != http.ErrServerClosed {
-			log.Fatalf("error: %v\n", err)
-		}
-	case <-sigChan:
-	}
-
-	server.Shutdown(context.Background())
-
-	<-done
-
-	if err := auth.ConnPool.Shutdown(); err != nil {
-		log.Fatalf("error: %v\n", err)
-	}
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/gemma/root.go	Tue Jul 24 17:45:17 2018 +0200
@@ -0,0 +1,132 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+	"os/signal"
+	"path/filepath"
+	"syscall"
+
+	"gemma.intevation.de/gemma/auth"
+	"gemma.intevation.de/gemma/config"
+	homedir "github.com/mitchellh/go-homedir"
+	"github.com/spf13/cobra"
+	"github.com/spf13/viper"
+)
+
+var rootCmd = &cobra.Command{
+	Use:   "gemma",
+	Short: "gemma is a server for waterway monitoring and management",
+	Run:   start,
+}
+
+func init() {
+	cobra.OnInitialize(initConfig)
+	fl := rootCmd.PersistentFlags
+	fl().StringVarP(&config.Config.File, "config", "c", "", "config file (default is $HOME/.gemma.toml)")
+	fl().StringVarP(&config.Config.DBHost, "dbhost", "H", "localhost", "host of the database")
+	fl().UintVarP(&config.Config.DBPort, "dbport", "P", 5432, "port of the database")
+	fl().StringVarP(&config.Config.DBName, "dbname", "d", "gemma", "name of the database")
+	fl().StringVarP(&config.Config.DBSSLMode, "dbssl", "S", "require", "SSL mode of the database")
+
+	fl().StringVarP(&config.Config.SessionStore, "sessions", "s", "", "path to the sessions file")
+
+	fl().StringVarP(&config.Config.Web, "web", "w", "", "path to the web files")
+	fl().StringVarP(&config.Config.WebHost, "host", "o", "localhost", "host of the web app")
+	fl().UintVarP(&config.Config.WebPort, "port", "p", 8000, "port of the web app")
+
+	viper.BindPFlag("dbhost", fl().Lookup("dbhost"))
+	viper.BindPFlag("dbport", fl().Lookup("dbport"))
+	viper.BindPFlag("dbname", fl().Lookup("dbname"))
+	viper.BindPFlag("dbssl", fl().Lookup("dbssl"))
+
+	viper.BindPFlag("sessions", fl().Lookup("sessions"))
+
+	viper.BindPFlag("web", fl().Lookup("web"))
+	viper.BindPFlag("host", fl().Lookup("host"))
+	viper.BindPFlag("port", fl().Lookup("port"))
+}
+
+func initConfig() {
+	// Don't forget to read config either from cfgFile or from home directory!
+	if config.Config.File != "" {
+		// Use config file from the flag.
+		viper.SetConfigFile(config.Config.File)
+	} else {
+		// Find home directory.
+		home, err := homedir.Dir()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		// Search config in home directory with name ".cobra" (without extension).
+		viper.AddConfigPath(home)
+		viper.SetConfigName(".gemma")
+	}
+	if err := viper.ReadInConfig(); err != nil {
+		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
+			// Don't bother if not found.
+			return
+		}
+		log.Fatalf("Can't read config: %v\n", err)
+	}
+}
+
+func start(cmd *cobra.Command, args []string) {
+
+	// Install connection pool
+	cp, err := auth.NewConnectionPool(config.Config.SessionStore)
+	if err != nil {
+		log.Fatalf("Error with session store: %v\n", err)
+	}
+	auth.ConnPool = cp
+
+	p, err := filepath.Abs(config.Config.Web)
+	if err != nil {
+		log.Fatalf("error: %v\n", err)
+	}
+	log.Printf("web: %s\n", p)
+	mux := http.NewServeMux()
+	mux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir(p))))
+	mux.HandleFunc("/api/token", token)
+	mux.Handle("/api/logout", auth.SessionMiddleware(http.HandlerFunc(token)))
+	mux.Handle("/api/renew", auth.SessionMiddleware(http.HandlerFunc(renew)))
+	mux.Handle("/api/create_user",
+		auth.SessionMiddleware(
+			auth.SessionChecker(http.HandlerFunc(createUser), auth.HasRole("sys_admin"))))
+
+	addr := fmt.Sprintf("%s:%d", config.Config.WebHost, config.Config.WebPort)
+	log.Printf("listen on %s\n", addr)
+
+	server := http.Server{Addr: addr, Handler: mux}
+
+	done := make(chan error)
+
+	go func() {
+		defer close(done)
+		done <- server.ListenAndServe()
+	}()
+
+	sigChan := make(chan os.Signal)
+	signal.Notify(sigChan, os.Interrupt, os.Kill, syscall.SIGTERM)
+
+	select {
+	case err := <-done:
+		if err != nil && err != http.ErrServerClosed {
+			log.Fatalf("error: %v\n", err)
+		}
+	case <-sigChan:
+	}
+
+	server.Shutdown(context.Background())
+
+	<-done
+
+	if err := auth.ConnPool.Shutdown(); err != nil {
+		log.Fatalf("error: %v\n", err)
+	}
+}
--- a/config/config.go	Tue Jul 24 16:19:05 2018 +0200
+++ b/config/config.go	Tue Jul 24 17:45:17 2018 +0200
@@ -1,48 +1,17 @@
 package config
 
-import (
-	"log"
-	"os"
-	"strconv"
-)
-
-var Config = NewConfiguration()
+var Config Configuration
 
 type Configuration struct {
+	File      string
 	DBHost    string
 	DBPort    uint
 	DBName    string
 	DBSSLMode string
 
 	SessionStore string
-}
 
-func NewConfiguration() *Configuration {
-	// TODO: Load from file.
-	return &Configuration{
-		DBHost:       envString("GEMMA_DB_HOST", "localhost"),
-		DBPort:       envUint("GEMMA_DB_PORT", 5432),
-		DBName:       envString("GEMMA_DB_NAME", "gemma"),
-		DBSSLMode:    envString("GEMMA_DB_SSL_MODE", "require"),
-		SessionStore: envString("GEMMA_SESSION_STORE", ""),
-	}
+	Web     string
+	WebHost string
+	WebPort uint
 }
-
-func envString(key, def string) string {
-	if v, ok := os.LookupEnv(key); ok {
-		return v
-	}
-	return def
-}
-
-func envUint(key string, def uint) uint {
-	if v, ok := os.LookupEnv(key); ok {
-		x, err := strconv.ParseUint(v, 10, 64)
-		if err != nil {
-			log.Printf("warn: invalid uint env %s: %v\n", key, err)
-			return def
-		}
-		return uint(x)
-	}
-	return def
-}