/*
Need to figure out timeouts for http service.
You might want to reset your DB_HOST env var during test tear down.
Add pulling git version from build command line.
Service should start even without a DB running yet.
symbols in profiles: https://github.com/golang/go/issues/23376 / https://github.com/google/pprof/pull/366
*/
Need to figure out timeouts for http service.
You might want to reset your DB_HOST env var during test tear down.
Add pulling git version from build command line.
Service should start even without a DB running yet.
/ * Need to figure out timeouts for http service . You might want to reset your DB_HOST env var during test tear down . Add pulling git version from build command line . Service should start even without a DB running yet . symbols in profiles : https : // github . com / golang / go / issues / 23376 / https : // github . com / google / pprof / pull / 366 * /
func main() {
	log := log.New(os.Stdout, "SALES : ", log.LstdFlags|log.Lmicroseconds|log.Lshortfile)
	var cfg struct {
		Web struct {
			APIHost         string        `default:"0.0.0.0:3000" envconfig:"API_HOST"`
			DebugHost       string        `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"`
			ReadTimeout     time.Duration `default:"5s" envconfig:"READ_TIMEOUT"`
			WriteTimeout    time.Duration `default:"5s" envconfig:"WRITE_TIMEOUT"`
			ShutdownTimeout time.Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"`
		}
		DB struct {
			DialTimeout time.Duration `default:"5s" envconfig:"DIAL_TIMEOUT"`
			Host        string        `default:"mongo:27017/gotraining" envconfig:"HOST"`
		}
		Trace struct {
			Host         string        `default:"http://tracer:3002/v1/publish" envconfig:"HOST"`
			BatchSize    int           `default:"1000" envconfig:"BATCH_SIZE"`
			SendInterval time.Duration `default:"15s" envconfig:"SEND_INTERVAL"`
			SendTimeout  time.Duration `default:"500ms" envconfig:"SEND_TIMEOUT"`
		}
		Auth struct {
			KeyID          string `envconfig:"KEY_ID"`
			PrivateKeyFile string `envconfig:"PRIVATE_KEY_FILE"`
			Algorithm      string `default:"RS256" envconfig:"ALGORITHM"`
		}
	}
	if err := envconfig.Process("SALES", &cfg); err != nil {
		log.Fatalf("main : Parsing Config : %v", err)
	}
	if err := flag.Process(&cfg); err != nil {
		if err != flag.ErrHelp {
			log.Fatalf("main : Parsing Command Line : %v", err)
		}
		return 
	}
	log.Println("main : Started : Application Initializing")
	defer log.Println("main : Completed")
	cfgJSON, err := json.MarshalIndent(cfg, "", "    ")
	if err != nil {
		log.Fatalf("main : Marshalling Config to JSON : %v", err)
	}
	log.Printf("main : Config : %v\n", string(cfgJSON))
	keyContents, err := ioutil.ReadFile(cfg.Auth.PrivateKeyFile)
	if err != nil {
		log.Fatalf("main : Reading auth private key : %v", err)
	}
	key, err := jwt.ParseRSAPrivateKeyFromPEM(keyContents)
	if err != nil {
		log.Fatalf("main : Parsing auth private key : %v", err)
	}
	publicKeyLookup := auth.NewSingleKeyFunc(cfg.Auth.KeyID, key.Public().(*rsa.PublicKey))
	authenticator, err := auth.NewAuthenticator(key, cfg.Auth.KeyID, cfg.Auth.Algorithm, publicKeyLookup)
	if err != nil {
		log.Fatalf("main : Constructing authenticator : %v", err)
	}
	log.Println("main : Started : Initialize Mongo")
	masterDB, err := db.New(cfg.DB.Host, cfg.DB.DialTimeout)
	if err != nil {
		log.Fatalf("main : Register DB : %v", err)
	}
	defer masterDB.Close()
	logger := func(format string, v ...interface{}) {
		log.Printf(format, v...)
	}
	log.Printf("main : Tracing Started : %s", cfg.Trace.Host)
	exporter, err := itrace.NewExporter(logger, cfg.Trace.Host, cfg.Trace.BatchSize, cfg.Trace.SendInterval, cfg.Trace.SendTimeout)
	if err != nil {
		log.Fatalf("main : RegiTracingster : ERROR : %v", err)
	}
	defer func() {
		log.Printf("main : Tracing Stopping : %s", cfg.Trace.Host)
		batch, err := exporter.Close()
		if err != nil {
			log.Printf("main : Tracing Stopped : ERROR : Batch[%d] : %v", batch, err)
		} else {
			log.Printf("main : Tracing Stopped : Flushed Batch[%d]", batch)
		}
	}()
	trace.RegisterExporter(exporter)
	trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
	debug := http.Server{
		Addr:           cfg.Web.DebugHost,
		Handler:        http.DefaultServeMux,
		ReadTimeout:    cfg.Web.ReadTimeout,
		WriteTimeout:   cfg.Web.WriteTimeout,
		MaxHeaderBytes: 1 << 20,
	}
	go func() {
		log.Printf("main : Debug Listening %s", cfg.Web.DebugHost)
		log.Printf("main : Debug Listener closed : %v", debug.ListenAndServe())
	}()
	api := http.Server{
		Addr:           cfg.Web.APIHost,
		Handler:        handlers.API(log, masterDB, authenticator),
		ReadTimeout:    cfg.Web.ReadTimeout,
		WriteTimeout:   cfg.Web.WriteTimeout,
		MaxHeaderBytes: 1 << 20,
	}
	serverErrors := make(chan error, 1)
	go func() {
		log.Printf("main : API Listening %s", cfg.Web.APIHost)
		serverErrors <- api.ListenAndServe()
	}()
	osSignals := make(chan os.Signal, 1)
	signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM)
	select {
	case err := <-serverErrors:
		log.Fatalf("main : Error starting server: %v", err)
	case <-osSignals:
		log.Println("main : Start shutdown...")
		ctx, cancel := context.WithTimeout(context.Background(), cfg.Web.ShutdownTimeout)
		defer cancel()
		if err := api.Shutdown(ctx); err != nil {
			log.Printf("main : Graceful shutdown did not complete in %v : %v", cfg.Web.ShutdownTimeout, err)
			if err := api.Close(); err != nil {
				log.Fatalf("main : Could not stop http server: %v", err)
			}
		}
	}
}
func main ( ) { log := log . New ( os . Stdout , " " , log . LstdFlags | log . Lmicroseconds | log . Lshortfile ) var cfg struct { Web struct { APIHost string `default:"0.0.0.0:3000" envconfig:"API_HOST"` DebugHost string `default:"0.0.0.0:4000" envconfig:"DEBUG_HOST"` ReadTimeout time . Duration `default:"5s" envconfig:"READ_TIMEOUT"` WriteTimeout time . Duration `default:"5s" envconfig:"WRITE_TIMEOUT"` ShutdownTimeout time . Duration `default:"5s" envconfig:"SHUTDOWN_TIMEOUT"` } DB struct { DialTimeout time . Duration `default:"5s" envconfig:"DIAL_TIMEOUT"` Host string `default:"mongo:27017/gotraining" envconfig:"HOST"` } Trace struct { Host string `default:"http://tracer:3002/v1/publish" envconfig:"HOST"` BatchSize int `default:"1000" envconfig:"BATCH_SIZE"` SendInterval time . Duration `default:"15s" envconfig:"SEND_INTERVAL"` SendTimeout time . Duration `default:"500ms" envconfig:"SEND_TIMEOUT"` } Auth struct { KeyID string `envconfig:"KEY_ID"` PrivateKeyFile string `envconfig:"PRIVATE_KEY_FILE"` Algorithm string `default:"RS256" envconfig:"ALGORITHM"` } } if err := envconfig . Process ( " " , & cfg ) ; err != nil { log . Fatalf ( " " , err ) } if err := flag . Process ( & cfg ) ; err != nil { if err != flag . ErrHelp { log . Fatalf ( " " , err ) } return } log . Println ( " " ) defer log . Println ( " " ) cfgJSON , err := json . MarshalIndent ( cfg , " " , " " ) if err != nil { log . Fatalf ( " " , err ) } log . Printf ( " \n " , string ( cfgJSON ) ) keyContents , err := ioutil . ReadFile ( cfg . Auth . PrivateKeyFile ) if err != nil { log . Fatalf ( " " , err ) } key , err := jwt . ParseRSAPrivateKeyFromPEM ( keyContents ) if err != nil { log . Fatalf ( " " , err ) } publicKeyLookup := auth . NewSingleKeyFunc ( cfg . Auth . KeyID , key . Public ( ) . ( * rsa . PublicKey ) ) authenticator , err := auth . NewAuthenticator ( key , cfg . Auth . KeyID , cfg . Auth . Algorithm , publicKeyLookup ) if err != nil { log . Fatalf ( " " , err ) } log . Println ( " " ) masterDB , err := db . New ( cfg . DB . Host , cfg . DB . DialTimeout ) if err != nil { log . Fatalf ( " " , err ) } defer masterDB . Close ( ) logger := func ( format string , v ... interface { } ) { log . Printf ( format , v ... ) } log . Printf ( " " , cfg . Trace . Host ) exporter , err := itrace . NewExporter ( logger , cfg . Trace . Host , cfg . Trace . BatchSize , cfg . Trace . SendInterval , cfg . Trace . SendTimeout ) if err != nil { log . Fatalf ( " " , err ) } defer func ( ) { log . Printf ( " " , cfg . Trace . Host ) batch , err := exporter . Close ( ) if err != nil { log . Printf ( " " , batch , err ) } else { log . Printf ( " " , batch ) } } ( ) trace . RegisterExporter ( exporter ) trace . ApplyConfig ( trace . Config { DefaultSampler : trace . AlwaysSample ( ) } ) debug := http . Server { Addr : cfg . Web . DebugHost , Handler : http . DefaultServeMux , ReadTimeout : cfg . Web . ReadTimeout , WriteTimeout : cfg . Web . WriteTimeout , MaxHeaderBytes : 1 << 20 , } go func ( ) { log . Printf ( " " , cfg . Web . DebugHost ) log . Printf ( " " , debug . ListenAndServe ( ) ) } ( ) api := http . Server { Addr : cfg . Web . APIHost , Handler : handlers . API ( log , masterDB , authenticator ) , ReadTimeout : cfg . Web . ReadTimeout , WriteTimeout : cfg . Web . WriteTimeout , MaxHeaderBytes : 1 << 20 , } serverErrors := make ( chan error , 1 ) go func ( ) { log . Printf ( " " , cfg . Web . APIHost ) serverErrors <- api . ListenAndServe ( ) } ( ) osSignals := make ( chan os . Signal , 1 ) signal . Notify ( osSignals , os . Interrupt , syscall . SIGTERM ) select { case err := <- serverErrors : log . Fatalf ( " " , err ) case <- osSignals : log . Println ( " " ) ctx , cancel := context . WithTimeout ( context . Background ( ) , cfg . Web . ShutdownTimeout ) defer cancel ( ) if err := api . Shutdown ( ctx ) ; err != nil { log . Printf ( " " , cfg . Web . ShutdownTimeout , err ) if err := api . Close ( ) ; err != nil { log . Fatalf ( " " , err ) } } } }
Need to figure out timeouts for http service.
Need to figure out timeouts for http service .
// ========================================================================= // Logging // ========================================================================= // Configuration // We displayed help. // ========================================================================= // App Starting // TODO: Validate what is being written to the logs. We don't // want to leak credentials or anything that can be a security risk. // ========================================================================= // Find auth keys // TODO(jlw) Do we really want to support rolling keys etc? We can use the naive NewSingleKeyFunc to keep things easy but leave in a note that says this is where you would need to add rotating key support. // TODO(jlw) Do we need to explicitly pass a public key in from the config? Since we already have the private key we can just compute the public key when needed. // ========================================================================= // Start Mongo // ========================================================================= // Start Tracing Support // ========================================================================= // Start Debug Service // /debug/vars - Added to the default mux by the expvars package. // /debug/pprof - Added to the default mux by the net/http/pprof package. // Not concerned with shutting this down when the // application is being shutdown. // ========================================================================= // Start API Service // Make a channel to listen for errors coming from the listener. Use a // buffered channel so the goroutine can exit if we don't collect this error. // Start the service listening for requests. // ========================================================================= // Shutdown // Make a channel to listen for an interrupt or terminate signal from the OS. // Use a buffered channel because the signal package requires it. // ========================================================================= // Stop API Service // Blocking main and waiting for shutdown. // Create context for Shutdown call. // Asking listener to shutdown and load shed.