Use go module as dependency tool instead of glide
[src/xds/xds-server.git] / lib / xdsserver / sdk.go
index e5d4d7c..cda334c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 "IoT.bzh"
+ * Copyright (C) 2017-2018 "IoT.bzh"
  * Author Sebastien Douheret <sebastien@iot.bzh>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,17 +20,16 @@ package xdsserver
 import (
        "encoding/json"
        "fmt"
-       "os"
        "os/exec"
        "path"
        "strconv"
        "strings"
        "time"
 
+       common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git"
+       "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git/eows"
+       "gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
        "github.com/Sirupsen/logrus"
-       common "github.com/iotbzh/xds-common/golib"
-       "github.com/iotbzh/xds-common/golib/eows"
-       "github.com/iotbzh/xds-server/lib/xsapiv1"
        uuid "github.com/satori/go.uuid"
 )
 
@@ -62,15 +61,21 @@ type CrossSDK struct {
        scripts    map[string]string
        installCmd *eows.ExecOverWS
        removeCmd  *eows.ExecOverWS
-
-       bufStdout string
-       bufStderr string
 }
 
 // ListCrossSDK List all available and installed SDK  (call "db-dump" script)
-func ListCrossSDK(scriptDir string, log *logrus.Logger) ([]xsapiv1.SDK, error) {
+func ListCrossSDK(scriptDir string, update bool, log *logrus.Logger) ([]xsapiv1.SDK, error) {
        sdksList := []xsapiv1.SDK{}
 
+       // First update sdk DB when requested
+       if update {
+               out, err := UpdateSDKDb(scriptDir, log)
+               if err != nil {
+                       log.Errorf("SDK DB update failure (%v): %v", err, out)
+                       return sdksList, fmt.Errorf("Error while updating SDK DB (%v)", err)
+               }
+       }
+
        // Retrieve SDKs list and info
        cmd := exec.Command(path.Join(scriptDir, scriptDbDump))
        stdout, err := cmd.CombinedOutput()
@@ -87,7 +92,7 @@ func ListCrossSDK(scriptDir string, log *logrus.Logger) ([]xsapiv1.SDK, error) {
 }
 
 // GetSDKInfo Used get-sdk-info script to extract SDK get info from a SDK file/tarball
-func GetSDKInfo(scriptDir, url, filename, md5sum string, log *logrus.Logger) (xsapiv1.SDK, error) {
+func GetSDKInfo(scriptDir, url, filename, md5sum, uuid string, log *logrus.Logger) (xsapiv1.SDK, error) {
        sdk := xsapiv1.SDK{}
 
        args := []string{}
@@ -101,6 +106,9 @@ func GetSDKInfo(scriptDir, url, filename, md5sum string, log *logrus.Logger) (xs
        } else {
                return sdk, fmt.Errorf("url of filename must be set")
        }
+       if uuid != "" {
+               args = append(args, "--uuid", uuid)
+       }
 
        cmd := exec.Command(path.Join(scriptDir, scriptGetSdkInfo), args...)
        stdout, err := cmd.CombinedOutput()
@@ -115,6 +123,15 @@ func GetSDKInfo(scriptDir, url, filename, md5sum string, log *logrus.Logger) (xs
        return sdk, nil
 }
 
+// UpdateSDKDb Used db-update script to update SDK database
+func UpdateSDKDb(scriptDir string, log *logrus.Logger) (string, error) {
+       args := []string{}
+       cmd := exec.Command(path.Join(scriptDir, scriptDbUpdate), args...)
+       stdout, err := cmd.CombinedOutput()
+
+       return string(stdout), err
+}
+
 // NewCrossSDK creates a new instance of CrossSDK
 func NewCrossSDK(ctx *Context, sdk xsapiv1.SDK, scriptDir string) (*CrossSDK, error) {
        s := CrossSDK{
@@ -203,7 +220,7 @@ func NewCrossSDK(ctx *Context, sdk xsapiv1.SDK, scriptDir string) (*CrossSDK, er
 // Install a SDK (non blocking command, IOW run in background)
 func (s *CrossSDK) Install(file string, force bool, timeout int, args []string, sess *ClientSession) error {
 
-       if s.sdk.Status == xsapiv1.SdkStatusInstalled {
+       if s.sdk.Status == xsapiv1.SdkStatusInstalled && !force {
                return fmt.Errorf("already installed")
        }
        if s.sdk.Status == xsapiv1.SdkStatusInstalling {
@@ -233,46 +250,36 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string,
        // Create new instance to execute command and sent output over WS
        s.installCmd = eows.New(s.scripts[scriptAdd], cmdArgs, sess.IOSocket, sess.ID, cmdID)
        s.installCmd.Log = s.Log
+       // TODO: enable Term s.installCmd.PtyMode = true
+       s.installCmd.LineTimeSpan = 500 * time.Millisecond.Nanoseconds()
        if timeout > 0 {
                s.installCmd.CmdExecTimeout = timeout
        } else {
                s.installCmd.CmdExecTimeout = 30 * 60 // default 30min
        }
 
-       // FIXME: temporary hack
-       s.bufStdout = ""
-       s.bufStderr = ""
-       SizeBufStdout := 10
-       SizeBufStderr := 2000
-       if valS, ok := os.LookupEnv("XDS_SDK_BUF_STDOUT"); ok {
-               if valI, err := strconv.Atoi(valS); err == nil {
-                       SizeBufStdout = valI
-               }
-       }
-       if valS, ok := os.LookupEnv("XDS_SDK_BUF_STDERR"); ok {
-               if valI, err := strconv.Atoi(valS); err == nil {
-                       SizeBufStderr = valI
-               }
-       }
-
        // Define callback for output (stdout+stderr)
-       s.installCmd.OutputCB = func(e *eows.ExecOverWS, stdout, stderr string) {
+       s.installCmd.OutputCB = func(e *eows.ExecOverWS, bStdout, bStderr []byte) {
+
+               stdout := string(bStdout)
+               stderr := string(bStderr)
+
                // paranoia
                data := e.UserData
                sdkID := (*data)["SDKID"].(string)
                if sdkID != s.sdk.ID {
-                       s.Log.Errorln("BUG: sdk ID differs: %v != %v", sdkID, s.sdk.ID)
+                       s.Log.Errorf("BUG: sdk ID differs: %v != %v", sdkID, s.sdk.ID)
                }
 
                // IO socket can be nil when disconnected
                so := s.sessions.IOSocketGet(e.Sid)
                if so == nil {
-                       s.Log.Infof("%s not emitted: WS closed (sid:%s, msgid:%s)", xsapiv1.EVTSDKInstall, e.Sid, e.CmdID)
+                       s.Log.Infof("%s not emitted: WS closed (sid:%s, msgid:%s)", xsapiv1.EVTSDKManagement, e.Sid, e.CmdID)
                        return
                }
 
                if s.LogLevelSilly {
-                       s.Log.Debugf("%s emitted - WS sid[4:] %s - id:%s - SDK ID:%s:", xsapiv1.EVTSDKInstall, e.Sid[4:], e.CmdID, sdkID[:16])
+                       s.Log.Debugf("%s emitted - WS sid[4:] %s - id:%s - SDK ID:%s:", xsapiv1.EVTSDKManagement, e.Sid[4:], e.CmdID, sdkID[:16])
                        if stdout != "" {
                                s.Log.Debugf("STDOUT <<%v>>", strings.Replace(stdout, "\n", "\\n", -1))
                        }
@@ -281,36 +288,30 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string,
                        }
                }
 
-               // Temporary "Hack": Buffered sent data to avoid freeze in web Browser
-               // FIXME: remove bufStdout & bufStderr and implement better algorithm
-               s.bufStdout += stdout
-               s.bufStderr += stderr
-               if len(s.bufStdout) > SizeBufStdout || len(s.bufStderr) > SizeBufStderr {
-                       // Emit event
-                       err := (*so).Emit(xsapiv1.EVTSDKInstall, xsapiv1.SDKManagementMsg{
-                               CmdID:     e.CmdID,
-                               Timestamp: time.Now().String(),
-                               Sdk:       s.sdk,
-                               Progress:  0, // TODO add progress
-                               Exited:    false,
-                               Stdout:    s.bufStdout,
-                               Stderr:    s.bufStderr,
-                       })
-                       if err != nil {
-                               s.Log.Errorf("WS Emit : %v", err)
-                       }
-                       s.bufStdout = ""
-                       s.bufStderr = ""
+               err := (*so).Emit(xsapiv1.EVTSDKManagement, xsapiv1.SDKManagementMsg{
+                       CmdID:     e.CmdID,
+                       Timestamp: time.Now().String(),
+                       Action:    xsapiv1.SdkMgtActionInstall,
+                       Sdk:       s.sdk,
+                       Progress:  0, // TODO add progress
+                       Exited:    false,
+                       Stdout:    stdout,
+                       Stderr:    stderr,
+               })
+               if err != nil {
+                       s.Log.Errorf("WS Emit : %v", err)
                }
        }
 
        // Define callback for output
        s.installCmd.ExitCB = func(e *eows.ExecOverWS, code int, exitError error) {
+               defer LockXdsUpdateCounter(s.Context, false)
+
                // paranoia
                data := e.UserData
                sdkID := (*data)["SDKID"].(string)
                if sdkID != s.sdk.ID {
-                       s.Log.Errorln("BUG: sdk ID differs: %v != %v", sdkID, s.sdk.ID)
+                       s.Log.Errorf("BUG: sdk ID differs: %v != %v", sdkID, s.sdk.ID)
                }
 
                s.Log.Infof("Command SDK ID %s [Cmd ID %s]  exited: code %d, exitError: %v", sdkID[:16], e.CmdID, code, exitError)
@@ -318,28 +319,10 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string,
                // IO socket can be nil when disconnected
                so := s.sessions.IOSocketGet(e.Sid)
                if so == nil {
-                       s.Log.Infof("%s (exit) not emitted - WS closed (id:%s)", xsapiv1.EVTSDKInstall, e.CmdID)
+                       s.Log.Infof("%s (exit) not emitted - WS closed (id:%s)", xsapiv1.EVTSDKManagement, e.CmdID)
                        return
                }
 
-               // Emit event remaining data in bufStdout/err
-               if len(s.bufStderr) > 0 || len(s.bufStdout) > 0 {
-                       err := (*so).Emit(xsapiv1.EVTSDKInstall, xsapiv1.SDKManagementMsg{
-                               CmdID:     e.CmdID,
-                               Timestamp: time.Now().String(),
-                               Sdk:       s.sdk,
-                               Progress:  50, // TODO add progress
-                               Exited:    false,
-                               Stdout:    s.bufStdout,
-                               Stderr:    s.bufStderr,
-                       })
-                       if err != nil {
-                               s.Log.Errorf("WS Emit : %v", err)
-                       }
-                       s.bufStdout = ""
-                       s.bufStderr = ""
-               }
-
                // Update SDK status
                if code == 0 && exitError == nil {
                        s.sdk.LastError = ""
@@ -349,8 +332,9 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string,
                        // (see sdks.go / monitorSDKInstallation )
                        // Update SetupFile when n
                        if s.sdk.SetupFile == "" {
-                               sdkDef, err := GetSDKInfo(s.sdk.FamilyConf.ScriptsDir, s.sdk.URL, "", "", s.Log)
+                               sdkDef, err := GetSDKInfo(s.sdk.FamilyConf.ScriptsDir, s.sdk.URL, "", "", s.sdk.UUID, s.Log)
                                if err != nil || sdkDef.SetupFile == "" {
+                                       s.Log.Errorf("GetSDKInfo error: %v", err)
                                        code = 1
                                        s.sdk.LastError = "Installation failed (cannot init SetupFile path)"
                                        s.sdk.Status = xsapiv1.SdkStatusNotInstalled
@@ -377,9 +361,10 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string,
                }
 
                // Emit event
-               errSoEmit := (*so).Emit(xsapiv1.EVTSDKInstall, xsapiv1.SDKManagementMsg{
+               errSoEmit := (*so).Emit(xsapiv1.EVTSDKManagement, xsapiv1.SDKManagementMsg{
                        CmdID:     e.CmdID,
                        Timestamp: time.Now().String(),
+                       Action:    xsapiv1.SdkMgtActionInstall,
                        Sdk:       s.sdk,
                        Progress:  100,
                        Exited:    true,
@@ -387,7 +372,12 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string,
                        Error:     emitErr,
                })
                if errSoEmit != nil {
-                       s.Log.Errorf("WS Emit : %v", errSoEmit)
+                       s.Log.Errorf("WS Emit EVTSDKManagement : %v", errSoEmit)
+               }
+
+               errSoEmit = s.events.Emit(xsapiv1.EVTSDKStateChange, s.sdk, e.Sid)
+               if errSoEmit != nil {
+                       s.Log.Errorf("WS Emit EVTSDKStateChange : %v", errSoEmit)
                }
 
                // Cleanup command for the next time
@@ -405,6 +395,10 @@ func (s *CrossSDK) Install(file string, force bool, timeout int, args []string,
        s.sdk.Status = xsapiv1.SdkStatusInstalling
        s.sdk.LastError = ""
 
+       if err := s.events.Emit(xsapiv1.EVTSDKStateChange, s.sdk, sess.ID); err != nil {
+               s.Log.Errorf("WS Emit EVTSDKStateChange installing : %v", err)
+       }
+
        err := s.installCmd.Start()
 
        return err
@@ -436,8 +430,8 @@ func (s *CrossSDK) Remove(timeout int, sess *ClientSession) error {
 
        s.sdk.Status = xsapiv1.SdkStatusUninstalling
 
-       // Emit Remove event
-       if err := (*so).Emit(xsapiv1.EVTSDKStateChange, s.sdk); err != nil {
+       // Notify state change
+       if err := s.events.Emit(xsapiv1.EVTSDKStateChange, s.sdk, sess.ID); err != nil {
                s.Log.Warningf("Cannot notify SDK remove: %v", err)
        }
 
@@ -445,43 +439,56 @@ func (s *CrossSDK) Remove(timeout int, sess *ClientSession) error {
        args := s.sdk.Path
        s.Log.Infof("Uninstall SDK %s: script=%v args=%v", s.sdk.Name, script, args)
 
+       // Notify start removing
+       evData := xsapiv1.SDKManagementMsg{
+               Timestamp: time.Now().String(),
+               Action:    xsapiv1.SdkMgtActionRemove,
+               Sdk:       s.sdk,
+               Progress:  0,
+               Exited:    false,
+               Code:      0,
+               Error:     "",
+       }
+       if errEmit := (*so).Emit(xsapiv1.EVTSDKManagement, evData); errEmit != nil {
+               s.Log.Warningf("Cannot notify EVTSDKManagement end: %v", errEmit)
+       }
+
+       // Run command to remove SDK
        cmd := exec.Command(script, args)
        stdout, err := cmd.CombinedOutput()
 
        s.sdk.Status = xsapiv1.SdkStatusNotInstalled
        s.Log.Debugf("SDK uninstall err %v, output:\n %v", err, string(stdout))
 
-       if err != nil {
-
-               // Emit Remove event
-               evData := xsapiv1.SDKManagementMsg{
-                       Timestamp: time.Now().String(),
-                       Sdk:       s.sdk,
-                       Progress:  100,
-                       Exited:    true,
-                       Code:      1,
-                       Error:     err.Error(),
-               }
-               if err := (*so).Emit(xsapiv1.EVTSDKRemove, evData); err != nil {
-                       s.Log.Warningf("Cannot notify SDK remove end: %v", err)
-               }
-
-               return fmt.Errorf("Error while uninstalling sdk: %v", err)
-       }
-
-       // Emit Remove event
-       evData := xsapiv1.SDKManagementMsg{
+       // Emit end of removing process
+       evData = xsapiv1.SDKManagementMsg{
                Timestamp: time.Now().String(),
+               Action:    xsapiv1.SdkMgtActionRemove,
                Sdk:       s.sdk,
                Progress:  100,
                Exited:    true,
                Code:      0,
                Error:     "",
        }
-       if err := (*so).Emit(xsapiv1.EVTSDKRemove, evData); err != nil {
-               s.Log.Warningf("Cannot notify SDK remove end: %v", err)
+
+       // Update error code on error
+       if err != nil {
+               evData.Code = 1
+               evData.Error = err.Error()
+       }
+
+       if errEmit := (*so).Emit(xsapiv1.EVTSDKManagement, evData); errEmit != nil {
+               s.Log.Warningf("Cannot notify EVTSDKManagement end: %v", errEmit)
        }
 
+       // Notify state change
+       if errEmit := s.events.Emit(xsapiv1.EVTSDKStateChange, s.sdk, sess.ID); errEmit != nil {
+               s.Log.Warningf("Cannot notify EVTSDKStateChange end: %v", errEmit)
+       }
+
+       if err != nil {
+               return fmt.Errorf("Error while uninstalling sdk: %v", err)
+       }
        return nil
 }
 
@@ -492,5 +499,9 @@ func (s *CrossSDK) Get() *xsapiv1.SDK {
 
 // GetEnvCmd returns the command used to initialized the environment
 func (s *CrossSDK) GetEnvCmd() []string {
+       if s.sdk.SetupFile == "" {
+               return []string{}
+       }
        return []string{"source", s.sdk.SetupFile}
+
 }