view cmd/gemma/root.go @ 226:63dd5216eee4

Refactored gemma server to be more REST-like.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 26 Jul 2018 12:24:30 +0200
parents 696c19abe869
children c2334b5d3dd0
line wrap: on
line source

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"

	"gemma.intevation.de/gemma/auth"
	"gemma.intevation.de/gemma/config"
	"gemma.intevation.de/gemma/controllers"

	"github.com/gorilla/mux"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"

	homedir "github.com/mitchellh/go-homedir"
)

var rootCmd = &cobra.Command{
	Use:   "gemma",
	Short: "gemma is a server for waterway monitoring and management",
	Run:   start,
}

// This is not part of the persistent config.
var configFile string

func init() {
	cobra.OnInitialize(initConfig)
	fl := rootCmd.PersistentFlags
	fl().StringVarP(&configFile, "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 configFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(configFile)
	} 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 && configFile == "" {
			// Don't bother if not found.
			return
		}
		log.Fatalf("Can't read config: %v\n", err)
	}
}

func prepareConnectionPool() {
	// 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
}

func injectViper(cmd *cobra.Command) {
	cmd.Flags().VisitAll(func(f *pflag.Flag) {
		if !f.Changed {
			if viper.IsSet(f.Name) {
				cmd.Flags().Set(f.Name, viper.GetString(f.Name))
			}
		}
	})
}

func start(cmd *cobra.Command, args []string) {

	// XXX: This hack is needed because we have our
	// own config storage.
	injectViper(cmd)

	web, err := filepath.Abs(config.Config.Web)
	if err != nil {
		log.Fatalf("error: %v\n", err)
	}

	prepareConnectionPool()

	m := mux.NewRouter()
	controllers.BindRoutes(m)

	m.PathPrefix("/").Handler(http.FileServer(http.Dir(web)))

	addr := fmt.Sprintf("%s:%d", config.Config.WebHost, config.Config.WebPort)
	log.Printf("listen on %s\n", addr)

	server := http.Server{Addr: addr, Handler: m}

	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)
	}
}