Skip to content
Snippets Groups Projects
main.go 5.94 KiB
Newer Older
  • Learn to ignore specific revisions
  • package main
    
    import (
    	"context"
    	"errors"
    	"log"
    
    	"net/http"
    	"time"
    
    	"github.com/kelseyhightower/envconfig"
    
    	"go.mongodb.org/mongo-driver/mongo"
    	"go.mongodb.org/mongo-driver/mongo/options"
    
    	"go.uber.org/zap"
    	"go.uber.org/zap/zapcore"
    	goahttp "goa.design/goa/v3/http"
    	goa "goa.design/goa/v3/pkg"
    	"golang.org/x/sync/errgroup"
    
    
    	"code.vereign.com/gaiax/tsa/golib/cache"
    
    	"code.vereign.com/gaiax/tsa/golib/graceful"
    	goahealth "code.vereign.com/gaiax/tsa/infohub/gen/health"
    	goahealthsrv "code.vereign.com/gaiax/tsa/infohub/gen/http/health/server"
    	goainfohubsrv "code.vereign.com/gaiax/tsa/infohub/gen/http/infohub/server"
    	goaopenapisrv "code.vereign.com/gaiax/tsa/infohub/gen/http/openapi/server"
    	goainfohub "code.vereign.com/gaiax/tsa/infohub/gen/infohub"
    	"code.vereign.com/gaiax/tsa/infohub/gen/openapi"
    
    	"code.vereign.com/gaiax/tsa/infohub/internal/clients/policy"
    	"code.vereign.com/gaiax/tsa/infohub/internal/clients/vault"
    
    	"code.vereign.com/gaiax/tsa/infohub/internal/config"
    
    	"code.vereign.com/gaiax/tsa/infohub/internal/credential"
    
    	"code.vereign.com/gaiax/tsa/infohub/internal/service"
    	"code.vereign.com/gaiax/tsa/infohub/internal/service/health"
    	"code.vereign.com/gaiax/tsa/infohub/internal/service/infohub"
    
    	"code.vereign.com/gaiax/tsa/infohub/internal/storage"
    
    )
    
    var Version = "0.0.0+development"
    
    func main() {
    	// load configuration from environment
    	var cfg config.Config
    	if err := envconfig.Process("", &cfg); err != nil {
    		log.Fatalf("cannot load configuration: %v", err)
    	}
    
    	// create logger
    	logger, err := createLogger(cfg.LogLevel)
    	if err != nil {
    		log.Fatalln(err)
    	}
    	defer logger.Sync() //nolint:errcheck
    
    	logger.Info("infohub service started", zap.String("version", Version), zap.String("goa", goa.Version()))
    
    
    	// connect to mongo db
    	db, err := mongo.Connect(
    		context.Background(),
    		options.Client().ApplyURI(cfg.Mongo.Addr).SetAuth(options.Credential{
    			Username: cfg.Mongo.User,
    			Password: cfg.Mongo.Pass,
    		}),
    	)
    	if err != nil {
    		logger.Fatal("error connecting to mongodb", zap.Error(err))
    	}
    	defer db.Disconnect(context.Background()) //nolint:errcheck
    
    	// create storage
    	storage, err := storage.New(db, cfg.Mongo.DB, cfg.Mongo.Collection, logger)
    	if err != nil {
    		logger.Fatal("error connecting to database", zap.Error(err))
    	}
    
    	vault, err := vault.New(cfg.Vault.Addr, cfg.Vault.Token, cfg.Vault.Keyname)
    	if err != nil {
    		logger.Fatal("error creating vault client", zap.Error(err))
    	}
    
    	httpClient := httpClient()
    	credentials := credential.NewIssuer(cfg.Credential.IssuerName, cfg.Credential.Keyname, vault, httpClient)
    
    	// create policy client
    	policy := policy.New(cfg.Policy.Addr, httpClient)
    
    	// create cache client
    	cache := cache.New(cfg.Cache.Addr)
    
    
    	// create services
    	var (
    		infohubSvc goainfohub.Service
    		healthSvc  goahealth.Service
    	)
    	{
    
    		infohubSvc = infohub.New(storage, policy, cache, credentials, logger)
    
    		healthSvc = health.New()
    	}
    
    	// create endpoints
    	var (
    		infohubEndpoints *goainfohub.Endpoints
    		healthEndpoints  *goahealth.Endpoints
    		openapiEndpoints *openapi.Endpoints
    	)
    	{
    		infohubEndpoints = goainfohub.NewEndpoints(infohubSvc)
    		healthEndpoints = goahealth.NewEndpoints(healthSvc)
    		openapiEndpoints = openapi.NewEndpoints(nil)
    	}
    
    	// Provide the transport specific request decoder and response encoder.
    	// The goa http package has built-in support for JSON, XML and gob.
    	// Other encodings can be used by providing the corresponding functions,
    	// see goa.design/implement/encoding.
    	var (
    		dec = goahttp.RequestDecoder
    		enc = goahttp.ResponseEncoder
    	)
    
    	// Build the service HTTP request multiplexer and configure it to serve
    	// HTTP requests to the service endpoints.
    	mux := goahttp.NewMuxer()
    
    	// Wrap the endpoints with the transport specific layers. The generated
    	// server packages contains code generated from the design which maps
    	// the service input and output data structures to HTTP requests and
    	// responses.
    	var (
    		infohubServer *goainfohubsrv.Server
    		healthServer  *goahealthsrv.Server
    		openapiServer *goaopenapisrv.Server
    	)
    	{
    		infohubServer = goainfohubsrv.New(infohubEndpoints, mux, dec, enc, nil, errFormatter)
    		healthServer = goahealthsrv.New(healthEndpoints, mux, dec, enc, nil, errFormatter)
    		openapiServer = goaopenapisrv.New(openapiEndpoints, mux, dec, enc, nil, errFormatter, nil, nil)
    	}
    
    	// Configure the mux.
    	goainfohubsrv.Mount(mux, infohubServer)
    	goahealthsrv.Mount(mux, healthServer)
    	goaopenapisrv.Mount(mux, openapiServer)
    
    	var handler http.Handler = mux
    	srv := &http.Server{
    		Addr:         cfg.HTTP.Host + ":" + cfg.HTTP.Port,
    		Handler:      handler,
    		IdleTimeout:  cfg.HTTP.IdleTimeout,
    		ReadTimeout:  cfg.HTTP.ReadTimeout,
    		WriteTimeout: cfg.HTTP.WriteTimeout,
    	}
    
    	g, ctx := errgroup.WithContext(context.Background())
    	g.Go(func() error {
    		if err := graceful.Shutdown(ctx, srv, 20*time.Second); err != nil {
    			logger.Error("server shutdown error", zap.Error(err))
    			return err
    		}
    		return errors.New("server stopped successfully")
    	})
    	if err := g.Wait(); err != nil {
    		logger.Error("run group stopped", zap.Error(err))
    	}
    
    	logger.Info("bye bye")
    }
    
    func createLogger(logLevel string, opts ...zap.Option) (*zap.Logger, error) {
    	var level = zapcore.InfoLevel
    	if logLevel != "" {
    		err := level.UnmarshalText([]byte(logLevel))
    		if err != nil {
    			return nil, err
    		}
    	}
    
    	config := zap.NewProductionConfig()
    	config.Level = zap.NewAtomicLevelAt(level)
    	config.DisableStacktrace = true
    	config.EncoderConfig.TimeKey = "ts"
    	config.EncoderConfig.EncodeTime = zapcore.RFC3339TimeEncoder
    	return config.Build(opts...)
    }
    
    func errFormatter(e error) goahttp.Statuser {
    	return service.NewErrorResponse(e)
    }
    
    
    func httpClient() *http.Client {
    	return &http.Client{
    		Transport: &http.Transport{
    			Proxy: http.ProxyFromEnvironment,
    			DialContext: (&net.Dialer{
    				Timeout: 30 * time.Second,
    			}).DialContext,
    			MaxIdleConns:        100,
    			MaxIdleConnsPerHost: 100,
    			TLSHandshakeTimeout: 10 * time.Second,
    			IdleConnTimeout:     60 * time.Second,
    		},
    		Timeout: 30 * time.Second,
    	}
    }