From ea3b17feb2eb2d54bbc27dc75eee60bd1fe67d27 Mon Sep 17 00:00:00 2001 From: Sebastien Douheret Date: Mon, 26 Jun 2017 18:54:13 +0200 Subject: Use xds-common go library. --- lib/apiv1/agent.go | 2 +- lib/apiv1/config.go | 2 +- lib/apiv1/exec.go | 2 +- lib/apiv1/folders.go | 2 +- lib/apiv1/make.go | 2 +- lib/apiv1/sdks.go | 2 +- lib/common/error.go | 16 --- lib/common/execPipeWs.go | 151 -------------------------- lib/common/filepath.go | 73 ------------- lib/common/httpclient.go | 257 -------------------------------------------- lib/crosssdk/sdks.go | 2 +- lib/model/folder.go | 2 +- lib/syncthing/st.go | 2 +- lib/xdsconfig/config.go | 2 +- lib/xdsconfig/fileconfig.go | 2 +- 15 files changed, 11 insertions(+), 508 deletions(-) delete mode 100644 lib/common/error.go delete mode 100644 lib/common/execPipeWs.go delete mode 100644 lib/common/filepath.go delete mode 100644 lib/common/httpclient.go (limited to 'lib') diff --git a/lib/apiv1/agent.go b/lib/apiv1/agent.go index b693608..651f246 100644 --- a/lib/apiv1/agent.go +++ b/lib/apiv1/agent.go @@ -8,7 +8,7 @@ import ( "path/filepath" "github.com/gin-gonic/gin" - "github.com/iotbzh/xds-agent/lib/common" + common "github.com/iotbzh/xds-common/golib" ) type XDSAgentTarball struct { diff --git a/lib/apiv1/config.go b/lib/apiv1/config.go index 326b6fa..662ec8e 100644 --- a/lib/apiv1/config.go +++ b/lib/apiv1/config.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/gin-gonic/gin" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" "github.com/iotbzh/xds-server/lib/xdsconfig" ) diff --git a/lib/apiv1/exec.go b/lib/apiv1/exec.go index 4994b27..654ff64 100644 --- a/lib/apiv1/exec.go +++ b/lib/apiv1/exec.go @@ -7,7 +7,7 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" ) // ExecArgs JSON parameters of /exec command diff --git a/lib/apiv1/folders.go b/lib/apiv1/folders.go index b4d2ac0..44bda24 100644 --- a/lib/apiv1/folders.go +++ b/lib/apiv1/folders.go @@ -5,7 +5,7 @@ import ( "strconv" "github.com/gin-gonic/gin" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" "github.com/iotbzh/xds-server/lib/xdsconfig" ) diff --git a/lib/apiv1/make.go b/lib/apiv1/make.go index 77f9ec3..5cd98c6 100644 --- a/lib/apiv1/make.go +++ b/lib/apiv1/make.go @@ -9,7 +9,7 @@ import ( "strconv" "github.com/gin-gonic/gin" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" ) // MakeArgs is the parameters (json format) of /make command diff --git a/lib/apiv1/sdks.go b/lib/apiv1/sdks.go index 5ae2b03..52af506 100644 --- a/lib/apiv1/sdks.go +++ b/lib/apiv1/sdks.go @@ -5,7 +5,7 @@ import ( "strconv" "github.com/gin-gonic/gin" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" ) // getSdks returns all SDKs configuration diff --git a/lib/common/error.go b/lib/common/error.go deleted file mode 100644 index 6873d82..0000000 --- a/lib/common/error.go +++ /dev/null @@ -1,16 +0,0 @@ -package common - -import ( - "fmt" - - "github.com/gin-gonic/gin" -) - -// APIError returns an uniform json formatted error -func APIError(c *gin.Context, format string, args ...interface{}) { - errMsg := fmt.Sprintf(format, args...) - c.JSON(500, gin.H{ - "status": "error", - "error": errMsg, - }) -} diff --git a/lib/common/execPipeWs.go b/lib/common/execPipeWs.go deleted file mode 100644 index 9bb4517..0000000 --- a/lib/common/execPipeWs.go +++ /dev/null @@ -1,151 +0,0 @@ -package common - -import ( - "bufio" - "fmt" - "io" - "os" - "strings" - "time" - - "syscall" - - "github.com/Sirupsen/logrus" - "github.com/googollee/go-socket.io" -) - -// EmitOutputCB is the function callback used to emit data -type EmitOutputCB func(sid string, cmdID int, stdout, stderr string, data *map[string]interface{}) - -// EmitExitCB is the function callback used to emit exit proc code -type EmitExitCB func(sid string, cmdID int, code int, err error, data *map[string]interface{}) - -// Inspired by : -// https://github.com/gorilla/websocket/blob/master/examples/command/main.go - -// ExecPipeWs executes a command and redirect stdout/stderr into a WebSocket -func ExecPipeWs(cmd []string, env []string, so *socketio.Socket, sid string, cmdID int, - cmdExecTimeout int, log *logrus.Logger, eoCB EmitOutputCB, eeCB EmitExitCB, data *map[string]interface{}) error { - - outr, outw, err := os.Pipe() - if err != nil { - return fmt.Errorf("Pipe stdout error: " + err.Error()) - } - - // XXX - do we need to pipe stdin one day ? - inr, inw, err := os.Pipe() - if err != nil { - outr.Close() - outw.Close() - return fmt.Errorf("Pipe stdin error: " + err.Error()) - } - - bashArgs := []string{"/bin/bash", "-c", strings.Join(cmd, " ")} - proc, err := os.StartProcess("/bin/bash", bashArgs, &os.ProcAttr{ - Files: []*os.File{inr, outw, outw}, - Env: append(os.Environ(), env...), - }) - if err != nil { - outr.Close() - outw.Close() - inr.Close() - inw.Close() - return fmt.Errorf("Process start error: " + err.Error()) - } - - go func() { - defer outr.Close() - defer outw.Close() - defer inr.Close() - defer inw.Close() - - stdoutDone := make(chan struct{}) - go cmdPumpStdout(so, outr, stdoutDone, sid, cmdID, log, eoCB, data) - - // Blocking function that poll input or wait for end of process - cmdPumpStdin(so, inw, proc, sid, cmdID, cmdExecTimeout, log, eeCB, data) - - // Some commands will exit when stdin is closed. - inw.Close() - - defer outr.Close() - - if status, err := proc.Wait(); err == nil { - // Other commands need a bonk on the head. - if !status.Exited() { - if err := proc.Signal(os.Interrupt); err != nil { - log.Errorln("Proc interrupt:", err) - } - - select { - case <-stdoutDone: - case <-time.After(time.Second): - // A bigger bonk on the head. - if err := proc.Signal(os.Kill); err != nil { - log.Errorln("Proc term:", err) - } - <-stdoutDone - } - } - } - }() - - return nil -} - -func cmdPumpStdin(so *socketio.Socket, w io.Writer, proc *os.Process, - sid string, cmdID int, tmo int, log *logrus.Logger, exitFuncCB EmitExitCB, - data *map[string]interface{}) { - /* XXX - code to add to support stdin through WS - for { - _, message, err := so. ?? ReadMessage() - if err != nil { - break - } - message = append(message, '\n') - if _, err := w.Write(message); err != nil { - break - } - } - */ - - // Monitor process exit - type DoneChan struct { - status int - err error - } - done := make(chan DoneChan, 1) - go func() { - status := 0 - sts, err := proc.Wait() - if !sts.Success() { - s := sts.Sys().(syscall.WaitStatus) - status = s.ExitStatus() - } - done <- DoneChan{status, err} - }() - - // Wait cmd complete - select { - case dC := <-done: - exitFuncCB(sid, cmdID, dC.status, dC.err, data) - case <-time.After(time.Duration(tmo) * time.Second): - exitFuncCB(sid, cmdID, -99, - fmt.Errorf("Exit Timeout for command ID %v", cmdID), data) - } -} - -func cmdPumpStdout(so *socketio.Socket, r io.Reader, done chan struct{}, - sid string, cmdID int, log *logrus.Logger, emitFuncCB EmitOutputCB, data *map[string]interface{}) { - defer func() { - }() - - sc := bufio.NewScanner(r) - for sc.Scan() { - emitFuncCB(sid, cmdID, string(sc.Bytes()), "", data) - } - if sc.Err() != nil { - log.Errorln("scan:", sc.Err()) - } - close(done) -} diff --git a/lib/common/filepath.go b/lib/common/filepath.go deleted file mode 100644 index 8101c5a..0000000 --- a/lib/common/filepath.go +++ /dev/null @@ -1,73 +0,0 @@ -package common - -import ( - "fmt" - "os" - "os/user" - "path" - "path/filepath" - "regexp" - "strings" -) - -// Exists returns whether the given file or directory exists or not -func Exists(path string) bool { - _, err := os.Stat(path) - if err == nil { - return true - } - if os.IsNotExist(err) { - return false - } - return true -} - -// ResolveEnvVar Resolved environment variable regarding the syntax ${MYVAR} -// or $MYVAR following by a slash or a backslash -func ResolveEnvVar(s string) (string, error) { - if s == "" { - return s, nil - } - - // Resolved tilde : ~/ - if len(s) > 2 && s[:2] == "~/" { - if usr, err := user.Current(); err == nil { - s = filepath.Join(usr.HomeDir, s[2:]) - } - } - - // Resolved ${MYVAR} - re := regexp.MustCompile("\\${([^}]+)}") - vars := re.FindAllStringSubmatch(s, -1) - res := s - for _, v := range vars { - val := os.Getenv(v[1]) - if val == "" { - return res, fmt.Errorf("ERROR: %s env variable not defined", v[1]) - } - - rer := regexp.MustCompile("\\${" + v[1] + "}") - res = rer.ReplaceAllString(res, val) - } - - // Resolved $MYVAR following by a slash (or a backslash for Windows) - // TODO - //re := regexp.MustCompile("\\$([^\\/])+/") - - return path.Clean(res), nil -} - -// PathNormalize -func PathNormalize(p string) string { - sep := string(filepath.Separator) - if sep != "/" { - return p - } - // Replace drive like C: by C/ - res := p - if p[1:2] == ":" { - res = p[0:1] + sep + p[2:] - } - res = strings.Replace(res, "\\", "/", -1) - return filepath.Clean(res) -} diff --git a/lib/common/httpclient.go b/lib/common/httpclient.go deleted file mode 100644 index 72132bf..0000000 --- a/lib/common/httpclient.go +++ /dev/null @@ -1,257 +0,0 @@ -package common - -import ( - "bytes" - "crypto/tls" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net/http" - "strings" - - "github.com/Sirupsen/logrus" -) - -type HTTPClient struct { - httpClient http.Client - endpoint string - apikey string - username string - password string - id string - csrf string - conf HTTPClientConfig - logger *logrus.Logger -} - -type HTTPClientConfig struct { - URLPrefix string - HeaderAPIKeyName string - Apikey string - HeaderClientKeyName string - CsrfDisable bool -} - -const ( - logError = 1 - logWarning = 2 - logInfo = 3 - logDebug = 4 -) - -// Inspired by syncthing/cmd/cli - -const insecure = false - -// HTTPNewClient creates a new HTTP client to deal with Syncthing -func HTTPNewClient(baseURL string, cfg HTTPClientConfig) (*HTTPClient, error) { - - // Create w new Http client - httpClient := http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: insecure, - }, - }, - } - client := HTTPClient{ - httpClient: httpClient, - endpoint: baseURL, - apikey: cfg.Apikey, - conf: cfg, - /* TODO - add user + pwd support - username: c.GlobalString("username"), - password: c.GlobalString("password"), - */ - } - - if client.apikey == "" { - if err := client.getCidAndCsrf(); err != nil { - return nil, err - } - } - return &client, nil -} - -// SetLogger Define the logger to use -func (c *HTTPClient) SetLogger(log *logrus.Logger) { - c.logger = log -} - -func (c *HTTPClient) log(level int, format string, args ...interface{}) { - if c.logger != nil { - switch level { - case logError: - c.logger.Errorf(format, args...) - break - case logWarning: - c.logger.Warningf(format, args...) - break - case logInfo: - c.logger.Infof(format, args...) - break - default: - c.logger.Debugf(format, args...) - break - } - } -} - -// Send request to retrieve Client id and/or CSRF token -func (c *HTTPClient) getCidAndCsrf() error { - request, err := http.NewRequest("GET", c.endpoint, nil) - if err != nil { - return err - } - if _, err := c.handleRequest(request); err != nil { - return err - } - if c.id == "" { - return errors.New("Failed to get device ID") - } - if !c.conf.CsrfDisable && c.csrf == "" { - return errors.New("Failed to get CSRF token") - } - return nil -} - -// GetClientID returns the id -func (c *HTTPClient) GetClientID() string { - return c.id -} - -// formatURL Build full url by concatenating all parts -func (c *HTTPClient) formatURL(endURL string) string { - url := c.endpoint - if !strings.HasSuffix(url, "/") { - url += "/" - } - url += strings.TrimLeft(c.conf.URLPrefix, "/") - if !strings.HasSuffix(url, "/") { - url += "/" - } - return url + strings.TrimLeft(endURL, "/") -} - -// HTTPGet Send a Get request to client and return an error object -func (c *HTTPClient) HTTPGet(url string, data *[]byte) error { - _, err := c.HTTPGetWithRes(url, data) - return err -} - -// HTTPGetWithRes Send a Get request to client and return both response and error -func (c *HTTPClient) HTTPGetWithRes(url string, data *[]byte) (*http.Response, error) { - request, err := http.NewRequest("GET", c.formatURL(url), nil) - if err != nil { - return nil, err - } - res, err := c.handleRequest(request) - if err != nil { - return res, err - } - if res.StatusCode != 200 { - return res, errors.New(res.Status) - } - - *data = c.responseToBArray(res) - - return res, nil -} - -// HTTPPost Send a POST request to client and return an error object -func (c *HTTPClient) HTTPPost(url string, body string) error { - _, err := c.HTTPPostWithRes(url, body) - return err -} - -// HTTPPostWithRes Send a POST request to client and return both response and error -func (c *HTTPClient) HTTPPostWithRes(url string, body string) (*http.Response, error) { - request, err := http.NewRequest("POST", c.formatURL(url), bytes.NewBufferString(body)) - if err != nil { - return nil, err - } - res, err := c.handleRequest(request) - if err != nil { - return res, err - } - if res.StatusCode != 200 { - return res, errors.New(res.Status) - } - return res, nil -} - -func (c *HTTPClient) responseToBArray(response *http.Response) []byte { - defer response.Body.Close() - bytes, err := ioutil.ReadAll(response.Body) - if err != nil { - // TODO improved error reporting - fmt.Println("ERROR: " + err.Error()) - } - return bytes -} - -func (c *HTTPClient) handleRequest(request *http.Request) (*http.Response, error) { - if c.conf.HeaderAPIKeyName != "" && c.apikey != "" { - request.Header.Set(c.conf.HeaderAPIKeyName, c.apikey) - } - if c.conf.HeaderClientKeyName != "" && c.id != "" { - request.Header.Set(c.conf.HeaderClientKeyName, c.id) - } - if c.username != "" || c.password != "" { - request.SetBasicAuth(c.username, c.password) - } - if c.csrf != "" { - request.Header.Set("X-CSRF-Token-"+c.id[:5], c.csrf) - } - - c.log(logDebug, "HTTP %s %v", request.Method, request.URL) - - response, err := c.httpClient.Do(request) - if err != nil { - return nil, err - } - - // Detect client ID change - cid := response.Header.Get(c.conf.HeaderClientKeyName) - if cid != "" && c.id != cid { - c.id = cid - } - - // Detect CSR token change - for _, item := range response.Cookies() { - if item.Name == "CSRF-Token-"+c.id[:5] { - c.csrf = item.Value - goto csrffound - } - } - // OK CSRF found -csrffound: - - if response.StatusCode == 404 { - return nil, errors.New("Invalid endpoint or API call") - } else if response.StatusCode == 401 { - return nil, errors.New("Invalid username or password") - } else if response.StatusCode == 403 { - if c.apikey == "" { - // Request a new Csrf for next requests - c.getCidAndCsrf() - return nil, errors.New("Invalid CSRF token") - } - return nil, errors.New("Invalid API key") - } else if response.StatusCode != 200 { - data := make(map[string]interface{}) - // Try to decode error field of APIError struct - json.Unmarshal(c.responseToBArray(response), &data) - if err, found := data["error"]; found { - return nil, fmt.Errorf(err.(string)) - } else { - body := strings.TrimSpace(string(c.responseToBArray(response))) - if body != "" { - return nil, fmt.Errorf(body) - } - } - return nil, errors.New("Unknown HTTP status returned: " + response.Status) - } - return response, nil -} diff --git a/lib/crosssdk/sdks.go b/lib/crosssdk/sdks.go index d08afc5..35a9998 100644 --- a/lib/crosssdk/sdks.go +++ b/lib/crosssdk/sdks.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/Sirupsen/logrus" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" "github.com/iotbzh/xds-server/lib/xdsconfig" ) diff --git a/lib/model/folder.go b/lib/model/folder.go index c73accc..56a46b1 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -3,7 +3,7 @@ package model import ( "fmt" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" "github.com/iotbzh/xds-server/lib/syncthing" "github.com/iotbzh/xds-server/lib/xdsconfig" ) diff --git a/lib/syncthing/st.go b/lib/syncthing/st.go index e4506a5..3380cda 100644 --- a/lib/syncthing/st.go +++ b/lib/syncthing/st.go @@ -20,7 +20,7 @@ import ( "regexp" "github.com/Sirupsen/logrus" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" "github.com/iotbzh/xds-server/lib/xdsconfig" "github.com/syncthing/syncthing/lib/config" ) diff --git a/lib/xdsconfig/config.go b/lib/xdsconfig/config.go index 5ada35f..f2d0710 100644 --- a/lib/xdsconfig/config.go +++ b/lib/xdsconfig/config.go @@ -7,7 +7,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" ) // Config parameters (json format) of /config command diff --git a/lib/xdsconfig/fileconfig.go b/lib/xdsconfig/fileconfig.go index 99e5382..90c1aad 100644 --- a/lib/xdsconfig/fileconfig.go +++ b/lib/xdsconfig/fileconfig.go @@ -8,7 +8,7 @@ import ( "path/filepath" "strings" - "github.com/iotbzh/xds-server/lib/common" + common "github.com/iotbzh/xds-common/golib" ) type SyncThingConf struct { -- cgit 1.2.3-korg