13 "github.com/Sirupsen/logrus"
14 "github.com/codegangsta/cli"
15 "github.com/iotbzh/xds-agent/lib/syncthing"
16 "github.com/iotbzh/xds-agent/lib/xdsconfig"
19 const cookieMaxAge = "3600"
21 // Context holds the Agent context structure
24 Config *xdsconfig.Config
31 xdsServers map[string]*XdsServer
39 // NewAgent Create a new instance of Agent
40 func NewAgent(cliCtx *cli.Context) *Context {
43 // Set logger level and formatter
44 log := cliCtx.App.Metadata["logger"].(*logrus.Logger)
46 logLevel := cliCtx.GlobalString("log")
48 logLevel = "error" // FIXME get from Config DefaultLogLevel
50 if log.Level, err = logrus.ParseLevel(logLevel); err != nil {
51 fmt.Printf("Invalid log level : \"%v\"\n", logLevel)
54 log.Formatter = &logrus.TextFormatter{}
56 // Define default configuration
58 ProgName: cliCtx.App.Name,
60 Exit: make(chan os.Signal, 1),
63 xdsServers: make(map[string]*XdsServer),
67 // register handler on SIGTERM / exit
68 signal.Notify(ctx.Exit, os.Interrupt, syscall.SIGTERM)
69 go handlerSigTerm(&ctx)
74 // Run Main function called to run agent
75 func (ctx *Context) Run() (int, error) {
78 // Logs redirected into a file when logfile option or logsDir config is set
79 ctx.Config.LogVerboseOut = os.Stderr
80 if ctx.Config.FileConf.LogsDir != "" {
81 if ctx.Config.Options.LogFile != "stdout" {
82 logFile := ctx.Config.Options.LogFile
84 fdL, err := os.OpenFile(logFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
86 msgErr := fmt.Errorf("Cannot create log file %s", logFile)
87 return int(syscall.EPERM), msgErr
91 ctx._logPrint("Logging file: %s\n", logFile)
94 logFileHTTPReq := filepath.Join(ctx.Config.FileConf.LogsDir, "xds-agent-verbose.log")
95 fdLH, err := os.OpenFile(logFileHTTPReq, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
97 msgErr := fmt.Errorf("Cannot create log file %s", logFileHTTPReq)
98 return int(syscall.EPERM), msgErr
100 ctx.Config.LogVerboseOut = fdLH
102 ctx._logPrint("Logging file for HTTP requests: %s\n", logFileHTTPReq)
105 // Create syncthing instance when section "syncthing" is present in config.json
106 if ctx.Config.FileConf.SThgConf != nil {
107 ctx.SThg = st.NewSyncThing(ctx.Config, ctx.Log)
110 // Start local instance of Syncthing and Syncthing-notify
112 ctx.Log.Infof("Starting Syncthing...")
113 ctx.SThgCmd, err = ctx.SThg.Start()
117 fmt.Printf("Syncthing started (PID %d)\n", ctx.SThgCmd.Process.Pid)
119 ctx.Log.Infof("Starting Syncthing-inotify...")
120 ctx.SThgInotCmd, err = ctx.SThg.StartInotify()
124 fmt.Printf("Syncthing-inotify started (PID %d)\n", ctx.SThgInotCmd.Process.Pid)
126 // Establish connection with local Syncthing (retry if connection fail)
127 time.Sleep(3 * time.Second)
131 if err := ctx.SThg.Connect(); err == nil {
134 ctx.Log.Infof("Establishing connection to Syncthing (retry %d/%d)", retry, maxRetry)
135 time.Sleep(time.Second)
138 if err != nil || retry == 0 {
142 // Retrieve Syncthing config
143 id, err := ctx.SThg.IDGet()
147 ctx.Log.Infof("Local Syncthing ID: %s", id)
150 ctx.Log.Infof("Cloud Sync / Syncthing not supported")
154 ctx.webServer = NewWebServer(ctx)
157 ctx.sessions = NewClientSessions(ctx, cookieMaxAge)
159 // Create events management
160 ctx.events = NewEvents(ctx)
162 // Create projects management
163 ctx.projects = NewProjects(ctx, ctx.SThg)
165 // Run Web Server until exit requested (blocking call)
166 if err = ctx.webServer.Serve(); err != nil {
171 return 4, fmt.Errorf("Program exited")
174 // Helper function to log message on both stdout and logger
175 func (ctx *Context) _logPrint(format string, args ...interface{}) {
176 fmt.Printf(format, args...)
177 if ctx.Log.Out != os.Stdout {
178 ctx.Log.Infof(format, args...)
182 // Handle exit and properly stop/close all stuff
183 func handlerSigTerm(ctx *Context) {
186 ctx.Log.Infof("Stoping Syncthing... (PID %d)",
187 ctx.SThgCmd.Process.Pid)
188 ctx.Log.Infof("Stoping Syncthing-inotify... (PID %d)",
189 ctx.SThgInotCmd.Process.Pid)
191 ctx.SThg.StopInotify()
193 if ctx.webServer != nil {
194 ctx.Log.Infof("Stoping Web server...")