diff --git a/manager/middleware.go b/manager/middleware.go index 3c2d1ea..67e9266 100644 --- a/manager/middleware.go +++ b/manager/middleware.go @@ -20,7 +20,7 @@ func contextErrorLogger(c *gin.Context) { c.Next() } -func (s *managerServer) workerIDValidator(c *gin.Context) { +func (s *Manager) workerIDValidator(c *gin.Context) { workerID := c.Param("id") _, err := s.adapter.GetWorker(workerID) if err != nil { diff --git a/manager/server.go b/manager/server.go index b2ab87b..8a38cfc 100644 --- a/manager/server.go +++ b/manager/server.go @@ -1,6 +1,7 @@ package manager import ( + "crypto/tls" "fmt" "net/http" @@ -14,13 +15,99 @@ const ( _infoKey = "message" ) -type managerServer struct { - *gin.Engine - adapter dbAdapter +var manager *Manager + +// A Manager represents a manager server +type Manager struct { + cfg *Config + engine *gin.Engine + adapter dbAdapter + tlsConfig *tls.Config +} + +// GetTUNASyncManager returns the manager from config +func GetTUNASyncManager(cfg *Config) *Manager { + if manager != nil { + return manager + } + + // create gin engine + if !cfg.Debug { + gin.SetMode(gin.ReleaseMode) + } + s := &Manager{ + cfg: cfg, + engine: gin.Default(), + adapter: nil, + tlsConfig: nil, + } + + if cfg.Files.CACert != "" { + tlsConfig, err := GetTLSConfig(cfg.Files.CACert) + if err != nil { + logger.Error("Error initializing TLS config: %s", err.Error()) + return nil + } + s.tlsConfig = tlsConfig + } + + if cfg.Files.DBFile != "" { + adapter, err := makeDBAdapter(cfg.Files.DBType, cfg.Files.DBFile) + if err != nil { + logger.Error("Error initializing DB adapter: %s", err.Error()) + return nil + } + s.setDBAdapter(adapter) + } + + // common log middleware + s.engine.Use(contextErrorLogger) + + s.engine.GET("/ping", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{_infoKey: "pong"}) + }) + // list jobs, status page + s.engine.GET("/jobs", s.listAllJobs) + + // list workers + s.engine.GET("/workers", s.listWorkers) + // worker online + s.engine.POST("/workers", s.registerWorker) + + // workerID should be valid in this route group + workerValidateGroup := s.engine.Group("/workers", s.workerIDValidator) + // get job list + workerValidateGroup.GET(":id/jobs", s.listJobsOfWorker) + // post job status + workerValidateGroup.POST(":id/jobs/:job", s.updateJobOfWorker) + + // for tunasynctl to post commands + s.engine.POST("/cmd", s.handleClientCmd) + + manager = s + return s +} + +func (s *Manager) setDBAdapter(adapter dbAdapter) { + s.adapter = adapter +} + +// Run runs the manager server forever +func (s *Manager) Run() { + addr := fmt.Sprintf("%s:%d", s.cfg.Server.Addr, s.cfg.Server.Port) + if s.cfg.Server.SSLCert == "" && s.cfg.Server.SSLKey == "" { + if err := s.engine.Run(addr); err != nil { + panic(err) + } + } else { + if err := s.engine.RunTLS(addr, s.cfg.Server.SSLCert, s.cfg.Server.SSLKey); err != nil { + panic(err) + } + } } // listAllJobs repond with all jobs of specified workers -func (s *managerServer) listAllJobs(c *gin.Context) { +func (s *Manager) listAllJobs(c *gin.Context) { mirrorStatusList, err := s.adapter.ListAllMirrorStatus() if err != nil { err := fmt.Errorf("failed to list all mirror status: %s", @@ -41,7 +128,7 @@ func (s *managerServer) listAllJobs(c *gin.Context) { } // listWrokers respond with informations of all the workers -func (s *managerServer) listWorkers(c *gin.Context) { +func (s *Manager) listWorkers(c *gin.Context) { var workerInfos []WorkerStatus workers, err := s.adapter.ListWorkers() if err != nil { @@ -63,7 +150,7 @@ func (s *managerServer) listWorkers(c *gin.Context) { } // registerWorker register an newly-online worker -func (s *managerServer) registerWorker(c *gin.Context) { +func (s *Manager) registerWorker(c *gin.Context) { var _worker WorkerStatus c.BindJSON(&_worker) newWorker, err := s.adapter.CreateWorker(_worker) @@ -80,7 +167,7 @@ func (s *managerServer) registerWorker(c *gin.Context) { } // listJobsOfWorker respond with all the jobs of the specified worker -func (s *managerServer) listJobsOfWorker(c *gin.Context) { +func (s *Manager) listJobsOfWorker(c *gin.Context) { workerID := c.Param("id") mirrorStatusList, err := s.adapter.ListMirrorStatus(workerID) if err != nil { @@ -94,13 +181,13 @@ func (s *managerServer) listJobsOfWorker(c *gin.Context) { c.JSON(http.StatusOK, mirrorStatusList) } -func (s *managerServer) returnErrJSON(c *gin.Context, code int, err error) { +func (s *Manager) returnErrJSON(c *gin.Context, code int, err error) { c.JSON(code, gin.H{ _errorKey: err.Error(), }) } -func (s *managerServer) updateJobOfWorker(c *gin.Context) { +func (s *Manager) updateJobOfWorker(c *gin.Context) { workerID := c.Param("id") var status MirrorStatus c.BindJSON(&status) @@ -117,7 +204,7 @@ func (s *managerServer) updateJobOfWorker(c *gin.Context) { c.JSON(http.StatusOK, newStatus) } -func (s *managerServer) handleClientCmd(c *gin.Context) { +func (s *Manager) handleClientCmd(c *gin.Context) { var clientCmd ClientCmd c.BindJSON(&clientCmd) workerID := clientCmd.WorkerID @@ -153,44 +240,3 @@ func (s *managerServer) handleClientCmd(c *gin.Context) { // TODO: check response for success c.JSON(http.StatusOK, gin.H{_infoKey: "successfully send command to worker " + workerID}) } - -func (s *managerServer) setDBAdapter(adapter dbAdapter) { - s.adapter = adapter -} - -func makeHTTPServer(debug bool) *managerServer { - // create gin engine - if !debug { - gin.SetMode(gin.ReleaseMode) - } - s := &managerServer{ - gin.Default(), - nil, - } - - // common log middleware - s.Use(contextErrorLogger) - - s.GET("/ping", func(c *gin.Context) { - c.JSON(http.StatusOK, gin.H{_infoKey: "pong"}) - }) - // list jobs, status page - s.GET("/jobs", s.listAllJobs) - - // list workers - s.GET("/workers", s.listWorkers) - // worker online - s.POST("/workers", s.registerWorker) - - // workerID should be valid in this route group - workerValidateGroup := s.Group("/workers", s.workerIDValidator) - // get job list - workerValidateGroup.GET(":id/jobs", s.listJobsOfWorker) - // post job status - workerValidateGroup.POST(":id/jobs/:job", s.updateJobOfWorker) - - // for tunasynctl to post commands - s.POST("/cmd", s.handleClientCmd) - - return s -} diff --git a/manager/server_test.go b/manager/server_test.go index beeb80b..11909ca 100644 --- a/manager/server_test.go +++ b/manager/server_test.go @@ -23,7 +23,7 @@ const ( func TestHTTPServer(t *testing.T) { Convey("HTTP server should work", t, func(ctx C) { InitLogger(true, true, false) - s := makeHTTPServer(false) + s := GetTUNASyncManager(&Config{Debug: false}) So(s, ShouldNotBeNil) s.setDBAdapter(&mockDBAdapter{ workerStore: map[string]WorkerStatus{ @@ -35,7 +35,7 @@ func TestHTTPServer(t *testing.T) { port := rand.Intn(10000) + 20000 baseURL := fmt.Sprintf("http://127.0.0.1:%d", port) go func() { - s.Run(fmt.Sprintf("127.0.0.1:%d", port)) + s.engine.Run(fmt.Sprintf("127.0.0.1:%d", port)) }() time.Sleep(50 * time.Microsecond) resp, err := http.Get(baseURL + "/ping")