Auto start Syncthing and Syncthing-inotify.
[src/xds/xds-server.git] / main.go
diff --git a/main.go b/main.go
index 40617d1..ba445f5 100644 (file)
--- a/main.go
+++ b/main.go
@@ -3,13 +3,20 @@
 package main
 
 import (
-       "log"
+       "fmt"
        "os"
+       "os/exec"
+       "os/signal"
+       "strings"
+       "syscall"
+       "time"
 
        "github.com/Sirupsen/logrus"
        "github.com/codegangsta/cli"
+       "github.com/iotbzh/xds-server/lib/model"
+       "github.com/iotbzh/xds-server/lib/syncthing"
+       "github.com/iotbzh/xds-server/lib/webserver"
        "github.com/iotbzh/xds-server/lib/xdsconfig"
-       "github.com/iotbzh/xds-server/lib/xdsserver"
 )
 
 const (
@@ -30,19 +37,150 @@ var AppVersion = "?.?.?"
 // Should be set by compilation -ldflags "-X main.AppSubVersion=xxx"
 var AppSubVersion = "unknown-dev"
 
-// Web server main routine
-func webServer(ctx *cli.Context) error {
+// Context holds the XDS server context
+type Context struct {
+       ProgName  string
+       Cli       *cli.Context
+       Config    *xdsconfig.Config
+       Log       *logrus.Logger
+       SThg      *st.SyncThing
+       SThgCmd   *exec.Cmd
+       MFolder   *model.Folder
+       WWWServer *webserver.ServerService
+       Exit      chan os.Signal
+}
+
+// NewContext Create a new instance of XDS server
+func NewContext(cliCtx *cli.Context) *Context {
+       var err error
+
+       // Set logger level and formatter
+       log := cliCtx.App.Metadata["logger"].(*logrus.Logger)
+
+       logLevel := cliCtx.GlobalString("log")
+       if logLevel == "" {
+               logLevel = "error" // FIXME get from Config DefaultLogLevel
+       }
+       if log.Level, err = logrus.ParseLevel(logLevel); err != nil {
+               fmt.Printf("Invalid log level : \"%v\"\n", logLevel)
+               os.Exit(1)
+       }
+       log.Formatter = &logrus.TextFormatter{}
+
+       // Define default configuration
+       ctx := Context{
+               ProgName: cliCtx.App.Name,
+               Cli:      cliCtx,
+               Log:      log,
+               Exit:     make(chan os.Signal, 1),
+       }
+
+       // register handler on SIGTERM / exit
+       signal.Notify(ctx.Exit, os.Interrupt, syscall.SIGTERM)
+       go handlerSigTerm(&ctx)
+
+       return &ctx
+}
+
+// Handle exit and properly stop/close all stuff
+func handlerSigTerm(ctx *Context) {
+       <-ctx.Exit
+       if ctx.SThg != nil {
+               ctx.Log.Infof("Stopping Syncthing... (PID %d)",
+                       ctx.SThgCmd.Process.Pid)
+               ctx.SThg.Stop()
+       }
+       if ctx.WWWServer != nil {
+               ctx.Log.Infof("Stoping Web server...")
+               ctx.WWWServer.Stop()
+       }
+       os.Exit(1)
+}
 
-       // Init config
-       cfg, err := xdsconfig.Init(ctx)
+// xdsServer main routine
+func xdsApp(cliCtx *cli.Context) error {
+       var err error
+
+       // Create XDS server context
+       ctx := NewContext(cliCtx)
+
+       // Load config
+       cfg, err := xdsconfig.Init(ctx.Cli, ctx.Log)
        if err != nil {
                return cli.NewExitError(err, 2)
        }
+       ctx.Config = cfg
+
+       // TODO allow to redirect stdout/sterr into logs file
+       //logFilename := filepath.Join(ctx.Config.FileConf.LogsDir + "xds-server.log")
+
+       // FIXME - add a builder interface and support other builder type (eg. native)
+       builderType := "syncthing"
+
+       switch builderType {
+       case "syncthing":
+
+               // Start local instance of Syncthing and Syncthing-notify
+               ctx.SThg = st.NewSyncThing(ctx.Config, ctx.Log)
+
+               ctx.Log.Infof("Starting Syncthing...")
+               ctx.SThgCmd, err = ctx.SThg.Start()
+               if err != nil {
+                       return cli.NewExitError(err, 2)
+               }
+               ctx.Log.Infof("Syncthing started (PID %d)", ctx.SThgCmd.Process.Pid)
+
+               // Establish connection with local Syncthing (retry if connection fail)
+               retry := 10
+               err = nil
+               for retry > 0 {
+                       if err = ctx.SThg.Connect(); err == nil {
+                               break
+                       }
+                       ctx.Log.Warningf("Establishing connection to Syncthing (retry %d/10)", retry)
+                       time.Sleep(time.Second)
+                       retry--
+               }
+               if err != nil || retry == 0 {
+                       return cli.NewExitError(err, 2)
+               }
+
+               // Retrieve Syncthing config
+               id, err := ctx.SThg.IDGet()
+               if err != nil {
+                       return cli.NewExitError(err, 2)
+               }
+
+               if ctx.Config.Builder, err = xdsconfig.NewBuilderConfig(id); err != nil {
+                       return cli.NewExitError(err, 2)
+               }
+
+               // Retrieve initial Syncthing config
+               stCfg, err := ctx.SThg.ConfigGet()
+               if err != nil {
+                       return cli.NewExitError(err, 2)
+               }
+               for _, stFld := range stCfg.Folders {
+                       relativePath := strings.TrimPrefix(stFld.RawPath, ctx.Config.ShareRootDir)
+                       if relativePath == "" {
+                               relativePath = stFld.RawPath
+                       }
+                       newFld := xdsconfig.NewFolderConfig(stFld.ID, stFld.Label, ctx.Config.ShareRootDir, strings.Trim(relativePath, "/"))
+                       ctx.Config.Folders = ctx.Config.Folders.Update(xdsconfig.FoldersConfig{newFld})
+               }
+
+               // Init model folder
+               ctx.MFolder = model.NewFolder(ctx.Config, ctx.SThg)
+
+       default:
+               err = fmt.Errorf("Unsupported builder type")
+               return cli.NewExitError(err, 3)
+       }
 
        // Create and start Web Server
-       svr := xdsserver.NewServer(cfg)
-       if err = svr.Serve(); err != nil {
-               log.Println(err)
+       ctx.WWWServer = webserver.NewServer(ctx.Config, ctx.MFolder, ctx.Log)
+       if err = ctx.WWWServer.Serve(); err != nil {
+               ctx.Log.Println(err)
                return cli.NewExitError(err, 3)
        }
 
@@ -83,7 +221,7 @@ func main() {
        }
 
        // only one action: Web Server
-       app.Action = webServer
+       app.Action = xdsApp
 
        app.Run(os.Args)
 }