@@ -11,6 +11,7 @@ import (
1111 "strings"
1212 "sync"
1313 "syscall"
14+ "time"
1415
1516 "github.com/feast-dev/feast/go/internal/feast"
1617 "github.com/feast-dev/feast/go/internal/feast/registry"
@@ -36,15 +37,28 @@ import (
3637
3738var tracer trace.Tracer
3839
40+ var newSignalStopChannel = func () (chan os.Signal , func ()) {
41+ stop := make (chan os.Signal , 1 )
42+ signal .Notify (stop , syscall .SIGINT , syscall .SIGTERM )
43+ return stop , func () {
44+ signal .Stop (stop )
45+ }
46+ }
47+
3948type ServerStarter interface {
4049 StartHttpServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions ) error
4150 StartGrpcServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions ) error
51+ StartHttpsServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions , certFile string , keyFile string ) error
4252}
4353
4454type RealServerStarter struct {}
4555
4656func (s * RealServerStarter ) StartHttpServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions ) error {
47- return StartHttpServer (fs , host , port , metricsPort , writeLoggedFeaturesCallback , loggingOpts )
57+ return StartHttpServer (fs , host , port , metricsPort , writeLoggedFeaturesCallback , loggingOpts , false , "" , "" )
58+ }
59+
60+ func (s * RealServerStarter ) StartHttpsServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions , certFile string , keyFile string ) error {
61+ return StartHttpServer (fs , host , port , metricsPort , writeLoggedFeaturesCallback , loggingOpts , true , certFile , keyFile )
4862}
4963
5064func (s * RealServerStarter ) StartGrpcServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions ) error {
@@ -58,18 +72,22 @@ func main() {
5872 port := 8080
5973 metricsPort := 9090
6074 server := RealServerStarter {}
75+ certFile := ""
76+ keyFile := ""
6177 // Current Directory
6278 repoPath , err := os .Getwd ()
6379 if err != nil {
6480 log .Error ().Stack ().Err (err ).Msg ("Failed to get current directory" )
6581 }
6682
67- flag .StringVar (& serverType , "type" , serverType , "Specify the server type (http or grpc)" )
83+ flag .StringVar (& serverType , "type" , serverType , "Specify the server type (http, https or grpc)" )
6884 flag .StringVar (& repoPath , "chdir" , repoPath , "Repository path where feature store yaml file is stored" )
6985
7086 flag .StringVar (& host , "host" , host , "Specify a host for the server" )
7187 flag .IntVar (& port , "port" , port , "Specify a port for the server" )
7288 flag .IntVar (& metricsPort , "metrics-port" , metricsPort , "Specify a port for the metrics server" )
89+ flag .StringVar (& certFile , "tls-cert-file" , "" , "Path to the TLS certificate file" )
90+ flag .StringVar (& keyFile , "tls-key-file" , "" , "Path to the TLS key file" )
7391 flag .Parse ()
7492
7593 // Initialize tracer
@@ -119,8 +137,10 @@ func main() {
119137 err = server .StartHttpServer (fs , host , port , metricsPort , nil , loggingOptions )
120138 } else if serverType == "grpc" {
121139 err = server .StartGrpcServer (fs , host , port , metricsPort , nil , loggingOptions )
140+ } else if serverType == "https" {
141+ err = server .StartHttpsServer (fs , host , port , metricsPort , nil , loggingOptions , certFile , keyFile )
122142 } else {
123- fmt .Println ("Unknown server type. Please specify 'http' or 'grpc'." )
143+ fmt .Println ("Unknown server type. Please specify 'http' or 'grpc' or 'https' ." )
124144 }
125145
126146 if err != nil {
@@ -227,27 +247,34 @@ func StartGrpcServer(fs *feast.FeatureStore, host string, port int, metricsPort
227247// StartHttpServerWithLogging starts HTTP server with enabled feature logging
228248// Go does not allow direct assignment to package-level functions as a way to
229249// mock them for tests
230- func StartHttpServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions ) error {
250+ func StartHttpServer (fs * feast.FeatureStore , host string , port int , metricsPort int , writeLoggedFeaturesCallback logging.OfflineStoreWriteCallback , loggingOpts * logging.LoggingOptions , httpsEnable bool , certFile string , keyFile string ) error {
251+ if httpsEnable && (certFile == "" || keyFile == "" ) {
252+ return fmt .Errorf ("--tls-cert-file and --tls-key-file must be provided for HTTPS server." )
253+ }
254+
231255 loggingService , err := constructLoggingService (fs , writeLoggedFeaturesCallback , loggingOpts )
232256 if err != nil {
233257 return err
234258 }
235259 ser := server .NewHttpServer (fs , loggingService )
236260 log .Info ().Msgf ("Starting a HTTP server on host %s, port %d" , host , port )
261+
237262 // Start metrics server
238- metricsServer := & http.Server {Addr : fmt .Sprintf (":%d" , metricsPort )}
263+ mux := http .NewServeMux ()
264+ mux .Handle ("/metrics" , promhttp .Handler ())
265+ metricsServer := & http.Server {
266+ Addr : fmt .Sprintf (":%d" , metricsPort ),
267+ Handler : mux ,
268+ }
239269 go func () {
240270 log .Info ().Msgf ("Starting metrics server on port %d" , metricsPort )
241- mux := http .NewServeMux ()
242- mux .Handle ("/metrics" , promhttp .Handler ())
243- metricsServer .Handler = mux
244271 if err := metricsServer .ListenAndServe (); err != nil && err != http .ErrServerClosed {
245272 log .Error ().Err (err ).Msg ("Failed to start metrics server" )
246273 }
247274 }()
248275
249- stop := make ( chan os. Signal , 1 )
250- signal . Notify ( stop , syscall . SIGINT , syscall . SIGTERM )
276+ stop , stopCleanup := newSignalStopChannel ( )
277+ defer stopCleanup ( )
251278
252279 var wg sync.WaitGroup
253280 wg .Add (1 )
@@ -263,7 +290,9 @@ func StartHttpServer(fs *feast.FeatureStore, host string, port int, metricsPort
263290 log .Error ().Err (err ).Msg ("Error when stopping the HTTP server" )
264291 }
265292 log .Info ().Msg ("Stopping metrics server..." )
266- if err := metricsServer .Shutdown (context .Background ()); err != nil {
293+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
294+ defer cancel ()
295+ if err := metricsServer .Shutdown (ctx ); err != nil {
267296 log .Error ().Err (err ).Msg ("Error stopping metrics server" )
268297 }
269298 if loggingService != nil {
@@ -279,7 +308,11 @@ func StartHttpServer(fs *feast.FeatureStore, host string, port int, metricsPort
279308 }
280309 }()
281310
282- err = ser .Serve (host , port )
311+ if httpsEnable {
312+ err = ser .ServeTLS (host , port , certFile , keyFile )
313+ } else {
314+ err = ser .Serve (host , port )
315+ }
283316 close (serverExited )
284317 wg .Wait ()
285318 return err
0 commit comments