X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=main.go;h=4fd49e9137a5fb59e0a4b5a7c663ce02b5f86ea5;hb=5e120c466686880c5bf6b94043dd01edc261fef9;hp=65617856b15992d0b6e8334a71b4ff687697a848;hpb=ec7051e1da665206f594c7616ad381bfeaea333a;p=src%2Fxds%2Fxds-server.git diff --git a/main.go b/main.go index 6561785..4fd49e9 100644 --- a/main.go +++ b/main.go @@ -3,19 +3,26 @@ package main import ( - "log" + "fmt" "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + "time" "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" + "github.com/iotbzh/xds-server/lib/crosssdk" + "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 ( appName = "xds-server" appDescription = "X(cross) Development System Server is a web server that allows to remotely cross build applications." - appVersion = "0.0.1" appCopyright = "Apache-2.0" appUsage = "X(cross) Development System Server" ) @@ -24,27 +31,189 @@ var appAuthors = []cli.Author{ cli.Author{Name: "Sebastien Douheret", Email: "sebastien@iot.bzh"}, } -// AppVersionGitTag is the git tag id added to version string -// Should be set by compilation -ldflags "-X main.AppVersionGitTag=xxx" -var AppVersionGitTag = "unknown-dev" +// AppVersion is the version of this application +var AppVersion = "?.?.?" -// Web server main routine -func webServer(ctx *cli.Context) error { +// AppSubVersion is the git tag id added to version string +// Should be set by compilation -ldflags "-X main.AppSubVersion=xxx" +var AppSubVersion = "unknown-dev" - // Init config - cfg, err := xdsconfig.Init(ctx) +// 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 + SThgInotCmd *exec.Cmd + MFolders *model.Folders + SDKs *crosssdk.SDKs + WWWServer *webserver.Server + 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("Stoping Syncthing... (PID %d)", ctx.SThgCmd.Process.Pid) + ctx.SThg.Stop() + ctx.Log.Infof("Stoping Syncthing-inotify... (PID %d)", ctx.SThgInotCmd.Process.Pid) + ctx.SThg.StopInotify() + } + if ctx.WWWServer != nil { + ctx.Log.Infof("Stoping Web server...") + ctx.WWWServer.Stop() + } + os.Exit(0) +} + +// XDS Server application 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) + return cli.NewExitError(err, -2) + } + ctx.Config = cfg + + // Logs redirected into a file when logsDir is set + logfilename := cliCtx.GlobalString("logfile") + ctx.Config.LogVerboseOut = os.Stderr + if ctx.Config.FileConf.LogsDir != "" { + if logfilename != "stdout" { + if logfilename == "" { + logfilename = "xds-server.log" + } + // is it an absolute path ? + logFile := logfilename + if logfilename[0] == '.' || logfilename[0] != '/' { + logFile = filepath.Join(ctx.Config.FileConf.LogsDir, logfilename) + } + fmt.Printf("Logging file: %s\n", logFile) + fdL, err := os.OpenFile(logFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666) + if err != nil { + msgErr := fmt.Sprintf("Cannot create log file %s", logFile) + return cli.NewExitError(msgErr, int(syscall.EPERM)) + } + ctx.Log.Out = fdL + } + + logFileHTTPReq := filepath.Join(ctx.Config.FileConf.LogsDir, "xds-server-verbose.log") + fmt.Printf("Logging file for HTTP requests: %s\n", logFileHTTPReq) + fdLH, err := os.OpenFile(logFileHTTPReq, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666) + if err != nil { + msgErr := fmt.Sprintf("Cannot create log file %s", logFileHTTPReq) + return cli.NewExitError(msgErr, int(syscall.EPERM)) + } + ctx.Config.LogVerboseOut = fdLH + } + + // Create syncthing instance when section "syncthing" is present in config.json + if ctx.Config.FileConf.SThgConf != nil { + ctx.SThg = st.NewSyncThing(ctx.Config, ctx.Log) + } + + // Start local instance of Syncthing and Syncthing-notify + if ctx.SThg != nil { + ctx.Log.Infof("Starting Syncthing...") + ctx.SThgCmd, err = ctx.SThg.Start() + if err != nil { + return cli.NewExitError(err, -4) + } + fmt.Printf("Syncthing started (PID %d)\n", ctx.SThgCmd.Process.Pid) + + ctx.Log.Infof("Starting Syncthing-inotify...") + ctx.SThgInotCmd, err = ctx.SThg.StartInotify() + if err != nil { + return cli.NewExitError(err, -4) + } + fmt.Printf("Syncthing-inotify started (PID %d)\n", ctx.SThgInotCmd.Process.Pid) + + // Establish connection with local Syncthing (retry if connection fail) + fmt.Printf("Establishing connection with Syncthing...\n") + time.Sleep(2 * time.Second) + maxRetry := 30 + retry := maxRetry + err = nil + for retry > 0 { + if err = ctx.SThg.Connect(); err == nil { + break + } + ctx.Log.Warningf("Establishing connection to Syncthing (retry %d/%d)", retry, maxRetry) + time.Sleep(time.Second) + retry-- + } + if err != nil || retry == 0 { + return cli.NewExitError(err, -4) + } + + // FIXME: do we still need Builder notion ? if no cleanup + if ctx.Config.Builder, err = xdsconfig.NewBuilderConfig(ctx.SThg.MyID); err != nil { + return cli.NewExitError(err, -4) + } + } + + // Init model folder + ctx.MFolders = model.FoldersNew(ctx.Config, ctx.SThg) + + // Load initial folders config from disk + if err := ctx.MFolders.LoadConfig(); err != nil { + return cli.NewExitError(err, -5) + } + + // Init cross SDKs + ctx.SDKs, err = crosssdk.Init(ctx.Config, ctx.Log) + if err != nil { + return cli.NewExitError(err, -6) } // Create and start Web Server - svr := xdsserver.NewServer(cfg) - if err = svr.Serve(); err != nil { - log.Println(err) - return cli.NewExitError(err, 3) + ctx.WWWServer = webserver.New(ctx.Config, ctx.MFolders, ctx.SDKs, ctx.Log) + if err = ctx.WWWServer.Serve(); err != nil { + ctx.Log.Println(err) + return cli.NewExitError(err, -7) } - return cli.NewExitError("Program exited ", 4) + return cli.NewExitError("Program exited ", -99) } // main @@ -58,12 +227,12 @@ func main() { app.Name = appName app.Description = appDescription app.Usage = appUsage - app.Version = appVersion + " (" + AppVersionGitTag + ")" + app.Version = AppVersion + " (" + AppSubVersion + ")" app.Authors = appAuthors app.Copyright = appCopyright app.Metadata = make(map[string]interface{}) - app.Metadata["version"] = appVersion - app.Metadata["git-tag"] = AppVersionGitTag + app.Metadata["version"] = AppVersion + app.Metadata["git-tag"] = AppSubVersion app.Metadata["logger"] = log app.Flags = []cli.Flag{ @@ -78,10 +247,21 @@ func main() { Usage: "logging level (supported levels: panic, fatal, error, warn, info, debug)\n\t", EnvVar: "LOG_LEVEL", }, + cli.StringFlag{ + Name: "logfile", + Value: "stdout", + Usage: "filename where logs will be redirected (default stdout)\n\t", + EnvVar: "LOG_FILENAME", + }, + cli.BoolFlag{ + Name: "no-folderconfig, nfc", + Usage: fmt.Sprintf("Do not read folder config file (%s)\n\t", xdsconfig.FoldersConfigFilename), + EnvVar: "NO_FOLDERCONFIG", + }, } // only one action: Web Server - app.Action = webServer + app.Action = xdsApp app.Run(os.Args) }