X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=main.go;h=8d1e0eb5b7e07673dfd0fbc52035128c01084aac;hb=aabf2c598db74783e62e81e4ca008ce47fe130ba;hp=197541d3c356d672421025bf5bcd676b575d6e76;hpb=e54535129f23970619042a328ad9a139bba21c5d;p=src%2Fxds%2Fxds-cli.git diff --git a/main.go b/main.go index 197541d..8d1e0eb 100644 --- a/main.go +++ b/main.go @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 "IoT.bzh" + * Copyright (C) 2017-2018 "IoT.bzh" * Author Sebastien Douheret * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,13 +26,14 @@ import ( "regexp" "sort" "strings" + "syscall" "text/tabwriter" + "gerrit.automotivelinux.org/gerrit/src/xds/xds-agent.git/lib/xaapiv1" + common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/golib" "github.com/Sirupsen/logrus" - "github.com/iotbzh/xds-agent/lib/xaapiv1" - common "github.com/iotbzh/xds-common/golib" + "github.com/joho/godotenv" - socketio_client "github.com/sebd71/go-socket.io-client" "github.com/urfave/cli" ) @@ -57,7 +58,7 @@ var AppSubVersion = "unknown-dev" // Application details const ( - appCopyright = "Copyright (C) 2017 IoT.bzh - Apache-2.0" + appCopyright = "Copyright (C) 2017-2018 IoT.bzh - Apache-2.0" defaultLogLevel = "error" ) @@ -70,19 +71,41 @@ var EnvConfFileMap map[string]string // HTTPCli Global variable that hold HTTP Client var HTTPCli *common.HTTPClient -// IOsk Global variable that hold SocketIo client -var IOsk *socketio_client.Client +// IOSkClient Global variable that hold SocketIo client +var IOSkClient *IOSockClient // exitError exists this program with the specified error func exitError(code int, f string, a ...interface{}) { + earlyDisplay() err := fmt.Sprintf(f, a...) fmt.Fprintf(os.Stderr, err+"\n") os.Exit(code) } +// earlyDebug Used to log info before logger has been initialized +var earlyDebug []string + +func earlyPrintf(format string, args ...interface{}) { + earlyDebug = append(earlyDebug, fmt.Sprintf(format, args...)) +} + +func earlyDisplay() { + for _, str := range earlyDebug { + Log.Infof("%s", str) + } + earlyDebug = []string{} +} + +// LogSillyf Logging helper used for silly logging (printed on log.debug) +func LogSillyf(format string, args ...interface{}) { + sillyVal, sillyLog := os.LookupEnv("XDS_LOG_SILLY") + if sillyLog && sillyVal == "1" { + Log.Debugf("SILLY: "+format, args...) + } +} + // main func main() { - var earlyDebug []string // Allow to set app name from cli (useful for debugging) if AppName == "" { @@ -126,6 +149,7 @@ func main() { app.Metadata["version"] = AppVersion app.Metadata["git-tag"] = AppSubVersion app.Metadata["logger"] = Log + app.EnableBashCompletion = true // Create env vars help dynDesc := "\nENVIRONMENT VARIABLES:" @@ -162,6 +186,12 @@ func main() { Usage: "logging level (supported levels: panic, fatal, error, warn, info, debug)", Value: defaultLogLevel, }, + cli.StringFlag{ + Name: "logfile", + Value: "stderr", + Usage: "filename where logs will be redirected (default stderr)\n\t", + EnvVar: "XDS_LOGFILENAME", + }, cli.StringFlag{ Name: "url, u", EnvVar: "XDS_AGENT_URL", @@ -187,8 +217,22 @@ func main() { initCmdProjects(&app.Commands) initCmdSdks(&app.Commands) initCmdExec(&app.Commands) + initCmdTargets(&app.Commands) initCmdMisc(&app.Commands) + // Add --config option to all commands to support --config option either before or after command verb + // IOW support following both syntaxes: + // xds-cli exec --config myPrj.conf ... + // xds-cli --config myPrj.conf exec ... + for i, cmd := range app.Commands { + if len(cmd.Flags) > 0 { + app.Commands[i].Flags = append(cmd.Flags, cli.StringFlag{Hidden: true, Name: "config, c"}) + } + for j, subCmd := range cmd.Subcommands { + app.Commands[i].Subcommands[j].Flags = append(subCmd.Flags, cli.StringFlag{Hidden: true, Name: "config, c"}) + } + } + sort.Sort(cli.FlagsByName(app.Flags)) sort.Sort(cli.CommandsByName(app.Commands)) @@ -204,12 +248,17 @@ func main() { // Load config file if requested if confFile != "" { - earlyDebug = append(earlyDebug, fmt.Sprintf("confFile detected: %v", confFile)) + earlyPrintf("confFile detected: %v", confFile) + confFile, err := common.ResolveEnvVar(confFile) + if err != nil { + exitError(1, "Error while resolving confFile: %v", err) + } + earlyPrintf("Resolved confFile: %v", confFile) if !common.Exists(confFile) { exitError(1, "Error env config file not found") } // Load config file variables that will overwrite env variables - err := godotenv.Overload(confFile) + err = godotenv.Overload(confFile) if err != nil { exitError(1, "Error loading env config file "+confFile) } @@ -219,7 +268,7 @@ func main() { if err != nil { exitError(1, "Error reading env config file "+confFile) } - earlyDebug = append(earlyDebug, fmt.Sprintf("EnvConfFileMap: %v", EnvConfFileMap)) + earlyPrintf("EnvConfFileMap: %v", EnvConfFileMap) } app.Before = func(ctx *cli.Context) error { @@ -244,10 +293,19 @@ func main() { } Log.Formatter = &logrus.TextFormatter{} - Log.Infof("%s version: %s", AppName, app.Version) - for _, str := range earlyDebug { - Log.Infof("%s", str) + if ctx.String("logfile") != "stderr" { + logFile, _ := common.ResolveEnvVar(ctx.String("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, 1) + } + Log.Infof("Logging to file: %s", logFile) + Log.Out = fdL } + + Log.Infof("%s version: %s", AppName, app.Version) + earlyDisplay() Log.Debugf("\nEnvironment: %v\n", os.Environ()) if err = XdsConnInit(ctx); err != nil { @@ -264,6 +322,24 @@ func main() { XdsConnClose() }() + // Start signals monitoring routine + MonitorSignals() + + // Default callback to handle interrupt signal + // Maybe be overwritten by some subcommand (eg. targets commands) + err := OnSignals(func(sig os.Signal) { + Log.Debugf("Send signal %v (from main)", sig) + if IsInterruptSignal(sig) { + err := cli.NewExitError("Interrupted\n", int(syscall.EINTR)) + cli.HandleExitCoder(err) + } + }) + if err != nil { + cli.NewExitError(err.Error(), 1) + return + } + + // Run the cli app app.Run(os.Args) } @@ -290,6 +366,11 @@ func XdsConnInit(ctx *cli.Context) error { serverURL = "http://" + serverURL } + lvl := common.HTTPLogLevelWarning + if Log.Level == logrus.DebugLevel { + lvl = common.HTTPLogLevelDebug + } + // Create HTTP client Log.Debugln("Connect HTTP client on ", agentURL) conf := common.HTTPClientConfig{ @@ -298,15 +379,22 @@ func XdsConnInit(ctx *cli.Context) error { CsrfDisable: true, LogOut: Log.Out, LogPrefix: "XDSAGENT: ", - LogLevel: common.HTTPLogLevelWarning, + LogLevel: lvl, } HTTPCli, err = common.HTTPNewClient(agentURL, conf) if err != nil { errmsg := err.Error() - if m, err := regexp.MatchString("Get http.?://", errmsg); m && err == nil { + m, err := regexp.MatchString("Get http.?://", errmsg) + if (m && err == nil) || strings.Contains(errmsg, "Failed to get device ID") { i := strings.LastIndex(errmsg, ":") - errmsg = "Cannot connection to " + agentURL + errmsg[i:] + newErr := "Cannot connection to " + agentURL + if i > 0 { + newErr += " (" + strings.TrimSpace(errmsg[i+1:]) + ")" + } else { + newErr += " (" + strings.TrimSpace(errmsg) + ")" + } + errmsg = newErr } return cli.NewExitError(errmsg, 1) } @@ -316,23 +404,17 @@ func XdsConnInit(ctx *cli.Context) error { // Create io Websocket client Log.Debugln("Connecting IO.socket client on ", agentURL) - opts := &socketio_client.Options{ - Transport: "websocket", - Header: make(map[string][]string), - } - opts.Header["XDS-AGENT-SID"] = []string{HTTPCli.GetClientID()} - - IOsk, err = socketio_client.NewClient(agentURL, opts) + IOSkClient, err = NewIoSocketClient(agentURL, HTTPCli.GetClientID()) if err != nil { - return cli.NewExitError("IO.socket connection error: "+err.Error(), 1) + return cli.NewExitError(err.Error(), 1) } - IOsk.On("error", func(err error) { + IOSkClient.On("error", func(err error) { fmt.Println("ERROR Websocket: ", err.Error()) }) ctx.App.Metadata["httpCli"] = HTTPCli - ctx.App.Metadata["ioskCli"] = IOsk + ctx.App.Metadata["ioskCli"] = IOSkClient // Display version in logs (debug helpers) ver := xaapiv1.XDSVersion{} @@ -346,9 +428,15 @@ func XdsConnInit(ctx *cli.Context) error { if err := XdsConfigGet(&xdsConf); err != nil { return cli.NewExitError("ERROR while getting XDS config: "+err.Error(), 1) } + if len(xdsConf.Servers) < 1 { + return cli.NewExitError("No XDS Server connected", 1) + } svrCfg := xdsConf.Servers[XdsServerIndexGet()] - if serverURL != "" && (svrCfg.URL != serverURL || !svrCfg.Connected) { - svrCfg.URL = serverURL + if (serverURL != "" && svrCfg.URL != serverURL) || !svrCfg.Connected { + Log.Infof("Update XDS Server config: serverURL=%v, svrCfg=%v", serverURL, svrCfg) + if serverURL != "" { + svrCfg.URL = serverURL + } svrCfg.ConnRetry = 10 if err := XdsConfigSet(xdsConf); err != nil { return cli.NewExitError("ERROR while updating XDS server URL: "+err.Error(), 1)