bitbucket.org/Aishee/synsec@v0.0.0-20210414005726-236fc01a153d/pkg/apiserver/apiserver.go (about)

     1  package apiserver
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"os"
     9  	"time"
    10  
    11  	"bitbucket.org/Aishee/synsec/pkg/apiserver/controllers"
    12  	"bitbucket.org/Aishee/synsec/pkg/csconfig"
    13  	"bitbucket.org/Aishee/synsec/pkg/database"
    14  	"bitbucket.org/Aishee/synsec/pkg/types"
    15  	"github.com/gin-gonic/gin"
    16  	"github.com/go-co-op/gocron"
    17  	"github.com/pkg/errors"
    18  	log "github.com/sirupsen/logrus"
    19  	"gopkg.in/tomb.v2"
    20  )
    21  
    22  var (
    23  	keyLength = 32
    24  )
    25  
    26  type APIServer struct {
    27  	URL            string
    28  	TLS            *csconfig.TLSCfg
    29  	dbClient       *database.Client
    30  	logFile        string
    31  	ctx            context.Context
    32  	controller     *controllers.Controller
    33  	flushScheduler *gocron.Scheduler
    34  	router         *gin.Engine
    35  	httpServer     *http.Server
    36  	apic           *apic
    37  	httpServerTomb tomb.Tomb
    38  }
    39  
    40  func NewServer(config *csconfig.LocalApiServerCfg) (*APIServer, error) {
    41  	var flushScheduler *gocron.Scheduler
    42  	dbClient, err := database.NewClient(config.DbConfig)
    43  	if err != nil {
    44  		return &APIServer{}, fmt.Errorf("unable to init database client: %s", err)
    45  	}
    46  
    47  	if config.DbConfig.Flush != nil {
    48  		flushScheduler, err = dbClient.StartFlushScheduler(config.DbConfig.Flush)
    49  		if err != nil {
    50  			return &APIServer{}, err
    51  		}
    52  	}
    53  
    54  	logFile := ""
    55  	if config.LogMedia == "file" {
    56  		logFile = fmt.Sprintf("%s/synsec_api.log", config.LogDir)
    57  	}
    58  
    59  	if log.GetLevel() < log.DebugLevel {
    60  		gin.SetMode(gin.ReleaseMode)
    61  	}
    62  	log.Debugf("starting router, logging to %s", logFile)
    63  	router := gin.New()
    64  	/* See https://github.com/gin-gonic/gin/pull/2474:
    65  	Gin does not handle safely X-Forwarded-For or X-Real-IP.
    66  	We do not trust them by default, but the user can opt-in
    67  	if they host LAPI behind a trusted proxy which sanitize
    68  	X-Forwarded-For and X-Real-IP.
    69  	*/
    70  	router.ForwardedByClientIP = config.UseForwardedForHeaders
    71  
    72  	/*The logger that will be used by handlers*/
    73  	clog := log.New()
    74  	if err := types.ConfigureLogger(clog); err != nil {
    75  		return nil, errors.Wrap(err, "while configuring gin logger")
    76  	}
    77  	if config.LogLevel != nil {
    78  		clog.SetLevel(*config.LogLevel)
    79  	}
    80  
    81  	gin.DefaultErrorWriter = clog.Writer()
    82  
    83  	// Logging to a file.
    84  	if logFile != "" {
    85  		file, err := os.Create(logFile)
    86  		if err != nil {
    87  			return &APIServer{}, errors.Wrapf(err, "creating api access log file: %s", logFile)
    88  		}
    89  		gin.DefaultWriter = io.MultiWriter(file)
    90  	}
    91  
    92  	router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
    93  		return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
    94  			param.ClientIP,
    95  			param.TimeStamp.Format(time.RFC1123),
    96  			param.Method,
    97  			param.Path,
    98  			param.Request.Proto,
    99  			param.StatusCode,
   100  			param.Latency,
   101  			param.Request.UserAgent(),
   102  			param.ErrorMessage,
   103  		)
   104  	}))
   105  
   106  	router.NoRoute(func(c *gin.Context) {
   107  		c.JSON(http.StatusNotFound, gin.H{"message": "Page or Method not found"})
   108  		return
   109  	})
   110  	router.Use(gin.Recovery())
   111  	controller := &controllers.Controller{
   112  		DBClient: dbClient,
   113  		Ectx:     context.Background(),
   114  		Router:   router,
   115  		Profiles: config.Profiles,
   116  		Log:      clog,
   117  	}
   118  
   119  	var apiClient *apic
   120  
   121  	if config.OnlineClient != nil && config.OnlineClient.Credentials != nil {
   122  		log.Printf("Loading CAPI pusher")
   123  		apiClient, err = NewAPIC(config.OnlineClient, dbClient)
   124  		if err != nil {
   125  			return &APIServer{}, err
   126  		}
   127  		controller.CAPIChan = apiClient.alertToPush
   128  	} else {
   129  		apiClient = nil
   130  		controller.CAPIChan = nil
   131  	}
   132  
   133  	if err := controller.Init(); err != nil {
   134  		return &APIServer{}, err
   135  	}
   136  
   137  	return &APIServer{
   138  		URL:            config.ListenURI,
   139  		TLS:            config.TLS,
   140  		logFile:        logFile,
   141  		dbClient:       dbClient,
   142  		controller:     controller,
   143  		flushScheduler: flushScheduler,
   144  		router:         router,
   145  		apic:           apiClient,
   146  		httpServerTomb: tomb.Tomb{},
   147  	}, nil
   148  
   149  }
   150  
   151  func (s *APIServer) Router() (*gin.Engine, error) {
   152  	return s.router, nil
   153  }
   154  
   155  func (s *APIServer) Run() error {
   156  	defer types.CatchPanic("lapi/runServer")
   157  
   158  	s.httpServer = &http.Server{
   159  		Addr:    s.URL,
   160  		Handler: s.router,
   161  	}
   162  
   163  	if s.apic != nil {
   164  		s.apic.pushTomb.Go(func() error {
   165  			if err := s.apic.Push(); err != nil {
   166  				log.Errorf("capi push: %s", err)
   167  				return err
   168  			}
   169  			return nil
   170  		})
   171  		s.apic.pullTomb.Go(func() error {
   172  			if err := s.apic.Pull(); err != nil {
   173  				log.Errorf("capi pull: %s", err)
   174  				return err
   175  			}
   176  			return nil
   177  		})
   178  		s.apic.metricsTomb.Go(func() error {
   179  			if err := s.apic.SendMetrics(); err != nil {
   180  				log.Errorf("capi metrics: %s", err)
   181  				return err
   182  			}
   183  			return nil
   184  		})
   185  	}
   186  
   187  	s.httpServerTomb.Go(func() error {
   188  		go func() {
   189  			if s.TLS != nil && s.TLS.CertFilePath != "" && s.TLS.KeyFilePath != "" {
   190  				if err := s.httpServer.ListenAndServeTLS(s.TLS.CertFilePath, s.TLS.KeyFilePath); err != nil {
   191  					log.Fatalf(err.Error())
   192  				}
   193  			} else {
   194  				if err := s.httpServer.ListenAndServe(); err != http.ErrServerClosed {
   195  					log.Fatalf(err.Error())
   196  				}
   197  			}
   198  		}()
   199  		<-s.httpServerTomb.Dying()
   200  		log.Infof("run: shutting down api server")
   201  		if err := s.Shutdown(); err != nil {
   202  			log.Errorf("while shutting down API Server : %s", err)
   203  			return err
   204  		}
   205  		return nil
   206  	})
   207  
   208  	return nil
   209  }
   210  
   211  func (s *APIServer) Close() {
   212  	if s.apic != nil {
   213  		s.apic.Shutdown() // stop apic first since it use dbClient
   214  	}
   215  	s.dbClient.Ent.Close()
   216  	if s.flushScheduler != nil {
   217  		s.flushScheduler.Stop()
   218  	}
   219  }
   220  
   221  func (s *APIServer) Shutdown() error {
   222  	s.Close()
   223  	if err := s.httpServer.Shutdown(context.TODO()); err != nil {
   224  		return err
   225  	}
   226  	return nil
   227  }