aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastien Douheret <sebastien.douheret@iot.bzh>2017-11-29 08:54:00 +0100
committerSebastien Douheret <sebastien.douheret@iot.bzh>2017-11-29 11:10:30 +0100
commit2f7828d01f4c4ca2909f95f098627cd5475ed225 (patch)
treeb5e71920b813b95cae3e32044be08b99223348ec
parent5caebfb4b7c3b73988f067082b219ce5b7f409ba (diff)
Refit source files to have a public xs-apiv1 lib package.
Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
-rw-r--r--.vscode/settings.json3
-rw-r--r--lib/apiv1/make.go215
-rw-r--r--lib/apiv1/version.go26
-rw-r--r--lib/crosssdk/sdk.go56
-rw-r--r--lib/syncthing/stfolder.go14
-rw-r--r--lib/xdsconfig/builderconfig.go15
-rw-r--r--lib/xdsconfig/config.go23
-rw-r--r--lib/xdsserver/apiv1-config.go (renamed from lib/apiv1/config.go)10
-rw-r--r--lib/xdsserver/apiv1-events.go (renamed from lib/apiv1/events.go)56
-rw-r--r--lib/xdsserver/apiv1-exec.go (renamed from lib/apiv1/exec.go)140
-rw-r--r--lib/xdsserver/apiv1-folders.go (renamed from lib/apiv1/folders.go)16
-rw-r--r--lib/xdsserver/apiv1-make.go214
-rw-r--r--lib/xdsserver/apiv1-sdks.go (renamed from lib/apiv1/sdks.go)2
-rw-r--r--lib/xdsserver/apiv1-version.go20
-rw-r--r--lib/xdsserver/apiv1.go (renamed from lib/apiv1/apiv1.go)28
-rw-r--r--lib/xdsserver/folder-interface.go22
-rw-r--r--lib/xdsserver/folder-pathmap.go (renamed from lib/folder/folder-pathmap.go)30
-rw-r--r--lib/xdsserver/folder-st-disable.go (renamed from lib/folder/folder-st-disable.go)22
-rw-r--r--lib/xdsserver/folder-st.go (renamed from lib/syncthing/folder-st.go)54
-rw-r--r--lib/xdsserver/folders.go (renamed from lib/model/folders.go)91
-rw-r--r--lib/xdsserver/sdk.go56
-rw-r--r--lib/xdsserver/sdks.go (renamed from lib/crosssdk/sdks.go)37
-rw-r--r--lib/xdsserver/sessions.go (renamed from lib/session/session.go)20
-rw-r--r--lib/xdsserver/webserver.go (renamed from lib/webserver/server.go)88
-rw-r--r--lib/xdsserver/xdsserver.go202
-rw-r--r--lib/xsapiv1/config.go18
-rw-r--r--lib/xsapiv1/events.go31
-rw-r--r--lib/xsapiv1/exec.go76
-rw-r--r--lib/xsapiv1/folders.go (renamed from lib/folder/folder-interface.go)21
-rw-r--r--lib/xsapiv1/sdks.go14
-rw-r--r--lib/xsapiv1/version.go9
-rw-r--r--main.go190
32 files changed, 918 insertions, 901 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 569ca52..5a12530 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -28,6 +28,7 @@
"rpath", "WSID", "sess", "IXDS", "xdsconfig", "xdsserver", "mfolder",
"inotify", "Inot", "pname", "pkill", "sdkid", "CLOUDSYNC", "xdsagent",
"gdbserver", "golib", "eows", "mfolders", "IFOLDER", "flds", "dflt",
- "stconfig", "reflectme", "franciscocpg"
+ "stconfig", "reflectme", "franciscocpg", "crosssdk", "urfave", "EXEPATH",
+ "conv"
]
}
diff --git a/lib/apiv1/make.go b/lib/apiv1/make.go
deleted file mode 100644
index 6e0c7d6..0000000
--- a/lib/apiv1/make.go
+++ /dev/null
@@ -1,215 +0,0 @@
-package apiv1
-
-import (
- "net/http"
- "strings"
-
- "time"
-
- "strconv"
-
- "github.com/gin-gonic/gin"
- common "github.com/iotbzh/xds-common/golib"
-)
-
-// MakeArgs is the parameters (json format) of /make command
-type MakeArgs struct {
- ID string `json:"id"`
- SdkID string `json:"sdkID"` // sdk ID to use for setting env
- CmdID string `json:"cmdID"` // command unique ID
- Args []string `json:"args"` // args to pass to make command
- Env []string `json:"env"`
- RPath string `json:"rpath"` // relative path into project
- ExitImmediate bool `json:"exitImmediate"` // when true, exit event sent immediately when command exited (IOW, don't wait file synchronization)
- CmdTimeout int `json:"timeout"` // command completion timeout in Second
-}
-
-// MakeOutMsg Message send on each output (stdout+stderr) of make command
-type MakeOutMsg struct {
- CmdID string `json:"cmdID"`
- Timestamp string `json:"timestamp"`
- Stdout string `json:"stdout"`
- Stderr string `json:"stderr"`
-}
-
-// MakeExitMsg Message send on make command exit
-type MakeExitMsg struct {
- CmdID string `json:"cmdID"`
- Timestamp string `json:"timestamp"`
- Code int `json:"code"`
- Error error `json:"error"`
-}
-
-// MakeOutEvent Event send in WS when characters are received on stdout/stderr
-const MakeOutEvent = "make:output"
-
-// MakeExitEvent Event send in WS when command exited
-const MakeExitEvent = "make:exit"
-
-var makeCommandID = 1
-
-func (s *APIService) buildMake(c *gin.Context) {
- var args MakeArgs
-
- if c.BindJSON(&args) != nil {
- common.APIError(c, "Invalid arguments")
- return
- }
-
- sess := s.sessions.Get(c)
- if sess == nil {
- common.APIError(c, "Unknown sessions")
- return
- }
- sop := sess.IOSocket
- if sop == nil {
- common.APIError(c, "Websocket not established")
- return
- }
-
- // Allow to pass id in url (/make/:id) or as JSON argument
- idArg := c.Param("id")
- if idArg == "" {
- idArg = args.ID
- }
- if idArg == "" {
- common.APIError(c, "Invalid id")
- return
- }
- id, err := s.mfolders.ResolveID(idArg)
- if err != nil {
- common.APIError(c, err.Error())
- return
- }
- pf := s.mfolders.Get(id)
- if pf == nil {
- common.APIError(c, "Unknown id")
- return
- }
- folder := *pf
- prj := folder.GetConfig()
-
- execTmo := args.CmdTimeout
- if execTmo == 0 {
- // TODO get default timeout from config.json file
- execTmo = 24 * 60 * 60 // 1 day
- }
-
- // TODO merge all code below with exec.go
-
- // Define callback for output
- var oCB common.EmitOutputCB
- oCB = func(sid string, cmdID string, stdout, stderr string, data *map[string]interface{}) {
- // IO socket can be nil when disconnected
- so := s.sessions.IOSocketGet(sid)
- if so == nil {
- s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%s", MakeOutEvent, sid, cmdID)
- return
- }
-
- // Retrieve project ID and RootPath
- prjID := (*data)["ID"].(string)
- prjRootPath := (*data)["RootPath"].(string)
-
- // Cleanup any references to internal rootpath in stdout & stderr
- stdout = strings.Replace(stdout, prjRootPath, "", -1)
- stderr = strings.Replace(stderr, prjRootPath, "", -1)
-
- s.log.Debugf("%s emitted - WS sid %s - id:%d - prjID:%s", MakeOutEvent, sid, id, prjID)
-
- // FIXME replace by .BroadcastTo a room
- err := (*so).Emit(MakeOutEvent, MakeOutMsg{
- CmdID: cmdID,
- Timestamp: time.Now().String(),
- Stdout: stdout,
- Stderr: stderr,
- })
- if err != nil {
- s.log.Errorf("WS Emit : %v", err)
- }
- }
-
- // Define callback for output
- eCB := func(sid string, cmdID string, code int, err error, data *map[string]interface{}) {
- s.log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", cmdID, code, err)
-
- // IO socket can be nil when disconnected
- so := s.sessions.IOSocketGet(sid)
- if so == nil {
- s.log.Infof("%s not emitted - WS closed (id:%s", MakeExitEvent, cmdID)
- return
- }
-
- // Retrieve project ID and RootPath
- prjID := (*data)["ID"].(string)
- exitImm := (*data)["ExitImmediate"].(bool)
-
- // XXX - workaround to be sure that Syncthing detected all changes
- if err := s.mfolders.ForceSync(prjID); err != nil {
- s.log.Errorf("Error while syncing folder %s: %v", prjID, err)
- }
- if !exitImm {
- // Wait end of file sync
- // FIXME pass as argument
- tmo := 60
- for t := tmo; t > 0; t-- {
- s.log.Debugf("Wait file insync for %s (%d/%d)", prjID, t, tmo)
- if sync, err := s.mfolders.IsFolderInSync(prjID); sync || err != nil {
- if err != nil {
- s.log.Errorf("ERROR IsFolderInSync (%s): %v", prjID, err)
- }
- break
- }
- time.Sleep(time.Second)
- }
- }
-
- // FIXME replace by .BroadcastTo a room
- e := (*so).Emit(MakeExitEvent, MakeExitMsg{
- CmdID: id,
- Timestamp: time.Now().String(),
- Code: code,
- Error: err,
- })
- if e != nil {
- s.log.Errorf("WS Emit : %v", e)
- }
- }
-
- // Unique ID for each commands
- if args.CmdID == "" {
- args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(makeCommandID)
- makeCommandID++
- }
- cmd := []string{}
-
- // Retrieve env command regarding Sdk ID
- if envCmd := s.sdks.GetEnvCmd(args.SdkID, prj.DefaultSdk); len(envCmd) > 0 {
- cmd = append(cmd, envCmd...)
- cmd = append(cmd, "&&")
- }
-
- cmd = append(cmd, "cd", folder.GetFullPath(args.RPath), "&&", "make")
- if len(args.Args) > 0 {
- cmd = append(cmd, args.Args...)
- }
-
- s.log.Debugf("Execute [Cmd ID %d]: %v", args.CmdID, cmd)
-
- data := make(map[string]interface{})
- data["ID"] = prj.ID
- data["RootPath"] = prj.RootPath
- data["ExitImmediate"] = args.ExitImmediate
-
- err = common.ExecPipeWs(cmd, args.Env, sop, sess.ID, args.CmdID, execTmo, s.log, oCB, eCB, &data)
- if err != nil {
- common.APIError(c, err.Error())
- return
- }
-
- c.JSON(http.StatusOK,
- gin.H{
- "status": "OK",
- "cmdID": args.CmdID,
- })
-}
diff --git a/lib/apiv1/version.go b/lib/apiv1/version.go
deleted file mode 100644
index 8f928ec..0000000
--- a/lib/apiv1/version.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package apiv1
-
-import (
- "net/http"
-
- "github.com/gin-gonic/gin"
-)
-
-type version struct {
- ID string `json:"id"`
- Version string `json:"version"`
- APIVersion string `json:"apiVersion"`
- VersionGitTag string `json:"gitTag"`
-}
-
-// getInfo : return various information about server
-func (s *APIService) getVersion(c *gin.Context) {
- response := version{
- ID: s.cfg.ServerUID,
- Version: s.cfg.Version,
- APIVersion: s.cfg.APIVersion,
- VersionGitTag: s.cfg.VersionGitTag,
- }
-
- c.JSON(http.StatusOK, response)
-}
diff --git a/lib/crosssdk/sdk.go b/lib/crosssdk/sdk.go
deleted file mode 100644
index 5be8954..0000000
--- a/lib/crosssdk/sdk.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package crosssdk
-
-import (
- "fmt"
- "path/filepath"
-
- uuid "github.com/satori/go.uuid"
-)
-
-// SDK Define a cross tool chain used to build application
-type SDK struct {
- ID string `json:"id" binding:"required"`
- Name string `json:"name"`
- Profile string `json:"profile"`
- Version string `json:"version"`
- Arch string `json:"arch"`
- Path string `json:"path"`
-
- // Not exported fields
- EnvFile string `json:"-"`
-}
-
-// NewCrossSDK creates a new instance of Syncthing
-func NewCrossSDK(path string) (*SDK, error) {
- // Assume that we have .../<profile>/<version>/<arch>
- s := SDK{Path: path}
-
- s.Arch = filepath.Base(path)
-
- d := filepath.Dir(path)
- s.Version = filepath.Base(d)
-
- d = filepath.Dir(d)
- s.Profile = filepath.Base(d)
-
- // Use V3 to ensure that we get same uuid on restart
- s.ID = uuid.NewV3(uuid.FromStringOrNil("sdks"), s.Profile+"_"+s.Arch+"_"+s.Version).String()
- s.Name = s.Arch + " (" + s.Version + ")"
-
- envFile := filepath.Join(path, "environment-setup*")
- ef, err := filepath.Glob(envFile)
- if err != nil {
- return nil, fmt.Errorf("Cannot retrieve environment setup file: %v", err)
- }
- if len(ef) != 1 {
- return nil, fmt.Errorf("No environment setup file found match %s", envFile)
- }
- s.EnvFile = ef[0]
-
- return &s, nil
-}
-
-// GetEnvCmd returns the command used to initialized the environment
-func (s *SDK) GetEnvCmd() []string {
- return []string{"source", s.EnvFile}
-}
diff --git a/lib/syncthing/stfolder.go b/lib/syncthing/stfolder.go
index 503ba4b..1a5a7ec 100644
--- a/lib/syncthing/stfolder.go
+++ b/lib/syncthing/stfolder.go
@@ -6,13 +6,13 @@ import (
"path/filepath"
"strings"
- "github.com/iotbzh/xds-server/lib/folder"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
stconfig "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
)
// FolderLoadFromStConfig Load/Retrieve folder config from syncthing database
-func (s *SyncThing) FolderLoadFromStConfig(f *[]folder.FolderConfig) error {
+func (s *SyncThing) FolderLoadFromStConfig(f *[]xsapiv1.FolderConfig) error {
defaultSdk := "" // cannot know which was the default sdk
@@ -36,15 +36,15 @@ func (s *SyncThing) FolderLoadFromStConfig(f *[]folder.FolderConfig) error {
if cliPath == "" {
cliPath = stFld.Path
}
- *f = append(*f, folder.FolderConfig{
+ *f = append(*f, xsapiv1.FolderConfig{
ID: stFld.ID,
Label: stFld.Label,
ClientPath: strings.TrimRight(cliPath, "/"),
- Type: folder.TypeCloudSync,
- Status: folder.StatusDisable,
+ Type: xsapiv1.TypeCloudSync,
+ Status: xsapiv1.StatusDisable,
DefaultSdk: defaultSdk,
RootPath: s.conf.FileConf.ShareRootDir,
- DataCloudSync: folder.CloudSyncConfig{SyncThingID: devID},
+ DataCloudSync: xsapiv1.CloudSyncConfig{SyncThingID: devID},
})
}
@@ -52,7 +52,7 @@ func (s *SyncThing) FolderLoadFromStConfig(f *[]folder.FolderConfig) error {
}
// FolderChange is called when configuration has changed
-func (s *SyncThing) FolderChange(f folder.FolderConfig) (string, error) {
+func (s *SyncThing) FolderChange(f xsapiv1.FolderConfig) (string, error) {
// Get current config
stCfg, err := s.ConfigGet()
diff --git a/lib/xdsconfig/builderconfig.go b/lib/xdsconfig/builderconfig.go
index 6fc1814..3b1ca55 100644
--- a/lib/xdsconfig/builderconfig.go
+++ b/lib/xdsconfig/builderconfig.go
@@ -3,24 +3,19 @@ package xdsconfig
import (
"errors"
"net"
-)
-// BuilderConfig represents the builder container configuration
-type BuilderConfig struct {
- IP string `json:"ip"`
- Port string `json:"port"`
- SyncThingID string `json:"syncThingID"`
-}
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
+)
// NewBuilderConfig creates a new BuilderConfig instance
-func NewBuilderConfig(stID string) (BuilderConfig, error) {
+func NewBuilderConfig(stID string) (xsapiv1.BuilderConfig, error) {
// Do we really need it ? may be not accessible from client side
ip, err := getLocalIP()
if err != nil {
- return BuilderConfig{}, err
+ return xsapiv1.BuilderConfig{}, err
}
- b := BuilderConfig{
+ b := xsapiv1.BuilderConfig{
IP: ip, // TODO currently not used
Port: "", // TODO currently not used
SyncThingID: stID,
diff --git a/lib/xdsconfig/config.go b/lib/xdsconfig/config.go
index 0fc1346..0201d40 100644
--- a/lib/xdsconfig/config.go
+++ b/lib/xdsconfig/config.go
@@ -9,16 +9,13 @@ import (
"github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
common "github.com/iotbzh/xds-common/golib"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
)
// Config parameters (json format) of /config command
type Config struct {
- ServerUID string `json:"id"`
- Version string `json:"version"`
- APIVersion string `json:"apiVersion"`
- VersionGitTag string `json:"gitTag"`
- SupportedSharing map[string]bool `json:"supportedSharing"`
- Builder BuilderConfig `json:"builder"`
+ // Public APIConfig fields
+ xsapiv1.APIConfig
// Private (un-exported fields in REST GET /config route)
Options Options `json:"-"`
@@ -64,12 +61,14 @@ func Init(cliCtx *cli.Context, log *logrus.Logger) (*Config, error) {
// Define default configuration
c := Config{
- ServerUID: uuid,
- Version: cliCtx.App.Metadata["version"].(string),
- APIVersion: DefaultAPIVersion,
- VersionGitTag: cliCtx.App.Metadata["git-tag"].(string),
- Builder: BuilderConfig{},
- SupportedSharing: map[string]bool{ /*FIXME USE folder.TypePathMap*/ "PathMap": true},
+ APIConfig: xsapiv1.APIConfig{
+ ServerUID: uuid,
+ Version: cliCtx.App.Metadata["version"].(string),
+ APIVersion: DefaultAPIVersion,
+ VersionGitTag: cliCtx.App.Metadata["git-tag"].(string),
+ Builder: xsapiv1.BuilderConfig{},
+ SupportedSharing: map[string]bool{ /*FIXME USE folder.TypePathMap*/ "PathMap": true},
+ },
Options: Options{
ConfigFile: cliCtx.GlobalString("config"),
diff --git a/lib/apiv1/config.go b/lib/xdsserver/apiv1-config.go
index 4b53217..5a5bb6e 100644
--- a/lib/apiv1/config.go
+++ b/lib/xdsserver/apiv1-config.go
@@ -1,4 +1,4 @@
-package apiv1
+package xdsserver
import (
"net/http"
@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
common "github.com/iotbzh/xds-common/golib"
- "github.com/iotbzh/xds-server/lib/xdsconfig"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
)
var confMut sync.Mutex
@@ -16,7 +16,7 @@ func (s *APIService) getConfig(c *gin.Context) {
confMut.Lock()
defer confMut.Unlock()
- c.JSON(http.StatusOK, s.cfg)
+ c.JSON(http.StatusOK, s.Config)
}
// SetConfig sets server configuration
@@ -24,7 +24,7 @@ func (s *APIService) setConfig(c *gin.Context) {
// FIXME - must be tested
c.JSON(http.StatusNotImplemented, "Not implemented")
- var cfgArg xdsconfig.Config
+ var cfgArg xsapiv1.APIConfig
if c.BindJSON(&cfgArg) != nil {
common.APIError(c, "Invalid arguments")
@@ -34,7 +34,7 @@ func (s *APIService) setConfig(c *gin.Context) {
confMut.Lock()
defer confMut.Unlock()
- s.log.Debugln("SET config: ", cfgArg)
+ s.Log.Debugln("SET config: ", cfgArg)
common.APIError(c, "Not Supported")
}
diff --git a/lib/apiv1/events.go b/lib/xdsserver/apiv1-events.go
index d837571..3823f9e 100644
--- a/lib/apiv1/events.go
+++ b/lib/xdsserver/apiv1-events.go
@@ -1,43 +1,13 @@
-package apiv1
+package xdsserver
import (
"net/http"
"strings"
"time"
- "github.com/iotbzh/xds-server/lib/folder"
-
"github.com/gin-gonic/gin"
common "github.com/iotbzh/xds-common/golib"
-)
-
-// EventArgs is the parameters (json format) of /events/register command
-type EventRegisterArgs struct {
- Name string `json:"name"`
- ProjectID string `json:"filterProjectID"`
-}
-
-type EventUnRegisterArgs struct {
- Name string `json:"name"`
- ID int `json:"id"`
-}
-
-// EventMsg Message send
-type EventMsg struct {
- Time string `json:"time"`
- Type string `json:"type"`
- Folder folder.FolderConfig `json:"folder"`
-}
-
-// EventEvent Event send in WS when an internal event (eg. Syncthing event is received)
-const (
- // EventTypePrefix Used as event prefix
- EventTypePrefix = "event:" // following by event type
-
- // Supported Events type
- EVTAll = EventTypePrefix + "all"
- EVTFolderChange = EventTypePrefix + "folder-change" // type EventMsg with Data type apiv1.???
- EVTFolderStateChange = EventTypePrefix + "folder-state-change" // type EventMsg with Data type apiv1.???
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
)
// eventsList Registering for events that will be send over a WS
@@ -47,7 +17,7 @@ func (s *APIService) eventsList(c *gin.Context) {
// eventsRegister Registering for events that will be send over a WS
func (s *APIService) eventsRegister(c *gin.Context) {
- var args EventRegisterArgs
+ var args xsapiv1.EventRegisterArgs
if c.BindJSON(&args) != nil {
common.APIError(c, "Invalid arguments")
@@ -60,7 +30,7 @@ func (s *APIService) eventsRegister(c *gin.Context) {
return
}
- evType := strings.TrimPrefix(EVTFolderStateChange, EventTypePrefix)
+ evType := strings.TrimPrefix(xsapiv1.EVTFolderStateChange, xsapiv1.EventTypePrefix)
if args.Name != evType {
common.APIError(c, "Unsupported event name")
return
@@ -102,8 +72,8 @@ func (s *APIService) eventsRegister(c *gin.Context) {
id, err := s.mfolders.SThg.Events.Register(args.Name, cbFunc, args.ProjectID, &data)
*/
- var cbFunc folder.EventCB
- cbFunc = func(cfg *folder.FolderConfig, data *folder.EventCBData) {
+ var cbFunc FolderEventCB
+ cbFunc = func(cfg *xsapiv1.FolderConfig, data *FolderEventCBData) {
ssid := (*data)["sid"].(string)
so := s.sessions.IOSocketGet(ssid)
if so == nil {
@@ -114,20 +84,20 @@ func (s *APIService) eventsRegister(c *gin.Context) {
return
}
- msg := EventMsg{
+ msg := xsapiv1.EventMsg{
Time: time.Now().String(),
Type: evType,
Folder: *cfg,
}
- s.log.Debugf("WS Emit %s - Status=%10s, IsInSync=%6v, ID=%s",
- EventTypePrefix+evType, cfg.Status, cfg.IsInSync, cfg.ID)
+ s.Log.Debugf("WS Emit %s - Status=%10s, IsInSync=%6v, ID=%s",
+ xsapiv1.EventTypePrefix+evType, cfg.Status, cfg.IsInSync, cfg.ID)
- if err := (*so).Emit(EventTypePrefix+evType, msg); err != nil {
- s.log.Errorf("WS Emit Folder StateChanged event : %v", err)
+ if err := (*so).Emit(xsapiv1.EventTypePrefix+evType, msg); err != nil {
+ s.Log.Errorf("WS Emit Folder StateChanged event : %v", err)
}
}
- data := make(folder.EventCBData)
+ data := make(FolderEventCBData)
data["sid"] = sess.ID
prjID, err := s.mfolders.ResolveID(args.ProjectID)
@@ -145,7 +115,7 @@ func (s *APIService) eventsRegister(c *gin.Context) {
// eventsRegister Registering for events that will be send over a WS
func (s *APIService) eventsUnRegister(c *gin.Context) {
- var args EventUnRegisterArgs
+ var args xsapiv1.EventUnRegisterArgs
if c.BindJSON(&args) != nil || args.Name == "" || args.ID < 0 {
common.APIError(c, "Invalid arguments")
diff --git a/lib/apiv1/exec.go b/lib/xdsserver/apiv1-exec.go
index baf431f..ce5e7b7 100644
--- a/lib/apiv1/exec.go
+++ b/lib/xdsserver/apiv1-exec.go
@@ -1,4 +1,4 @@
-package apiv1
+package xdsserver
import (
"fmt"
@@ -12,91 +12,17 @@ import (
"github.com/gin-gonic/gin"
common "github.com/iotbzh/xds-common/golib"
"github.com/iotbzh/xds-common/golib/eows"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
"github.com/kr/pty"
)
-type (
- // ExecArgs JSON parameters of /exec command
- ExecArgs struct {
- ID string `json:"id" binding:"required"`
- SdkID string `json:"sdkID"` // sdk ID to use for setting env
- CmdID string `json:"cmdID"` // command unique ID
- Cmd string `json:"cmd" binding:"required"`
- Args []string `json:"args"`
- Env []string `json:"env"`
- RPath string `json:"rpath"` // relative path into project
- TTY bool `json:"tty"` // Use a tty, specific to gdb --tty option
- TTYGdbserverFix bool `json:"ttyGdbserverFix"` // Set to true to activate gdbserver workaround about inferior output
- ExitImmediate bool `json:"exitImmediate"` // when true, exit event sent immediately when command exited (IOW, don't wait file synchronization)
- CmdTimeout int `json:"timeout"` // command completion timeout in Second
- }
-
- // ExecRes JSON result of /exec command
- ExecRes struct {
- Status string `json:"status"` // status OK
- CmdID string `json:"cmdID"` // command unique ID
- }
-
- // ExecSigRes JSON result of /signal command
- ExecSigRes struct {
- Status string `json:"status"` // status OK
- CmdID string `json:"cmdID"` // command unique ID
- }
-
- // ExecInMsg Message used to received input characters (stdin)
- ExecInMsg struct {
- CmdID string `json:"cmdID"`
- Timestamp string `json:"timestamp"`
- Stdin string `json:"stdin"`
- }
-
- // ExecOutMsg Message used to send output characters (stdout+stderr)
- ExecOutMsg struct {
- CmdID string `json:"cmdID"`
- Timestamp string `json:"timestamp"`
- Stdout string `json:"stdout"`
- Stderr string `json:"stderr"`
- }
-
- // ExecExitMsg Message sent when executed command exited
- ExecExitMsg struct {
- CmdID string `json:"cmdID"`
- Timestamp string `json:"timestamp"`
- Code int `json:"code"`
- Error error `json:"error"`
- }
-
- // ExecSignalArgs JSON parameters of /exec/signal command
- ExecSignalArgs struct {
- CmdID string `json:"cmdID" binding:"required"` // command id
- Signal string `json:"signal" binding:"required"` // signal number
- }
-)
-
-const (
- // ExecInEvent Event send in WS when characters are sent (stdin)
- ExecInEvent = "exec:input"
-
- // ExecOutEvent Event send in WS when characters are received (stdout or stderr)
- ExecOutEvent = "exec:output"
-
- // ExecExitEvent Event send in WS when program exited
- ExecExitEvent = "exec:exit"
-
- // ExecInferiorInEvent Event send in WS when characters are sent to an inferior (used by gdb inferior/tty)
- ExecInferiorInEvent = "exec:inferior-input"
-
- // ExecInferiorOutEvent Event send in WS when characters are received by an inferior
- ExecInferiorOutEvent = "exec:inferior-output"
-)
-
var execCommandID = 1
// ExecCmd executes remotely a command
func (s *APIService) execCmd(c *gin.Context) {
var gdbPty, gdbTty *os.File
var err error
- var args ExecArgs
+ var args xsapiv1.ExecArgs
if c.BindJSON(&args) != nil {
common.APIError(c, "Invalid arguments")
return
@@ -180,19 +106,19 @@ func (s *APIService) execCmd(c *gin.Context) {
return
}
- s.log.Debugf("Client command tty: %v %v\n", gdbTty.Name(), gdbTty.Name())
+ s.Log.Debugf("Client command tty: %v %v\n", gdbTty.Name(), gdbTty.Name())
cmdArgs = append(cmdArgs, "--tty="+gdbTty.Name())
}
// Unique ID for each commands
if args.CmdID == "" {
- args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(execCommandID)
+ args.CmdID = s.Config.ServerUID[:18] + "_" + strconv.Itoa(execCommandID)
execCommandID++
}
// Create new execution over WS context
execWS := eows.New(strings.Join(cmd, " "), cmdArgs, sop, sess.ID, args.CmdID)
- execWS.Log = s.log
+ execWS.Log = s.Log
// Append client project dir to environment
execWS.Env = append(args.Env, "CLIENT_PROJECT_DIR="+prj.ClientPath)
@@ -207,9 +133,9 @@ func (s *APIService) execCmd(c *gin.Context) {
}
// Define callback for input (stdin)
- execWS.InputEvent = ExecInEvent
+ execWS.InputEvent = xsapiv1.ExecInEvent
execWS.InputCB = func(e *eows.ExecOverWS, stdin string) (string, error) {
- s.log.Debugf("STDIN <<%v>>", strings.Replace(stdin, "\n", "\\n", -1))
+ s.Log.Debugf("STDIN <<%v>>", strings.Replace(stdin, "\n", "\\n", -1))
// Handle Ctrl-D
if len(stdin) == 1 && stdin == "\x04" {
@@ -223,7 +149,7 @@ func (s *APIService) execCmd(c *gin.Context) {
prjID := (*data)["ID"].(string)
f := s.mfolders.Get(prjID)
if f == nil {
- s.log.Errorf("InputCB: Cannot get folder ID %s", prjID)
+ s.Log.Errorf("InputCB: Cannot get folder ID %s", prjID)
} else {
// Translate paths from client to server
stdin = (*f).ConvPathCli2Svr(stdin)
@@ -237,7 +163,7 @@ func (s *APIService) execCmd(c *gin.Context) {
// 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)", ExecOutEvent, e.Sid, e.CmdID)
+ s.Log.Infof("%s not emitted: WS closed (sid:%s, msgid:%s)", xsapiv1.ExecOutEvent, e.Sid, e.CmdID)
return
}
@@ -248,30 +174,30 @@ func (s *APIService) execCmd(c *gin.Context) {
f := s.mfolders.Get(prjID)
if f == nil {
- s.log.Errorf("OutputCB: Cannot get folder ID %s", prjID)
+ s.Log.Errorf("OutputCB: Cannot get folder ID %s", prjID)
} else {
// Translate paths from server to client
stdout = (*f).ConvPathSvr2Cli(stdout)
stderr = (*f).ConvPathSvr2Cli(stderr)
}
- s.log.Debugf("%s emitted - WS sid[4:] %s - id:%s - prjID:%s", ExecOutEvent, e.Sid[4:], e.CmdID, prjID)
+ s.Log.Debugf("%s emitted - WS sid[4:] %s - id:%s - prjID:%s", xsapiv1.ExecOutEvent, e.Sid[4:], e.CmdID, prjID)
if stdout != "" {
- s.log.Debugf("STDOUT <<%v>>", strings.Replace(stdout, "\n", "\\n", -1))
+ s.Log.Debugf("STDOUT <<%v>>", strings.Replace(stdout, "\n", "\\n", -1))
}
if stderr != "" {
- s.log.Debugf("STDERR <<%v>>", strings.Replace(stderr, "\n", "\\n", -1))
+ s.Log.Debugf("STDERR <<%v>>", strings.Replace(stderr, "\n", "\\n", -1))
}
// FIXME replace by .BroadcastTo a room
- err := (*so).Emit(ExecOutEvent, ExecOutMsg{
+ err := (*so).Emit(xsapiv1.ExecOutEvent, xsapiv1.ExecOutMsg{
CmdID: e.CmdID,
Timestamp: time.Now().String(),
Stdout: stdout,
Stderr: stderr,
})
if err != nil {
- s.log.Errorf("WS Emit : %v", err)
+ s.Log.Errorf("WS Emit : %v", err)
}
// XXX - Workaround due to gdbserver bug that doesn't redirect
@@ -289,27 +215,27 @@ func (s *APIService) execCmd(c *gin.Context) {
out = strings.Replace(out, "\\r", "\r", -1)
out = strings.Replace(out, "\\t", "\t", -1)
- s.log.Debugf("STDOUT INFERIOR: <<%v>>", out)
- err := (*so).Emit(ExecInferiorOutEvent, ExecOutMsg{
+ s.Log.Debugf("STDOUT INFERIOR: <<%v>>", out)
+ err := (*so).Emit(xsapiv1.ExecInferiorOutEvent, xsapiv1.ExecOutMsg{
CmdID: e.CmdID,
Timestamp: time.Now().String(),
Stdout: out,
Stderr: "",
})
if err != nil {
- s.log.Errorf("WS Emit : %v", err)
+ s.Log.Errorf("WS Emit : %v", err)
}
}
}
} else {
- s.log.Errorf("INFERIOR out parsing error: stdout=<%v>", stdout)
+ s.Log.Errorf("INFERIOR out parsing error: stdout=<%v>", stdout)
}
}
}
// Define callback for output
execWS.ExitCB = func(e *eows.ExecOverWS, code int, err error) {
- s.log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", e.CmdID, code, err)
+ s.Log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", e.CmdID, code, err)
// Close client tty
defer func() {
@@ -324,7 +250,7 @@ func (s *APIService) execCmd(c *gin.Context) {
// IO socket can be nil when disconnected
so := s.sessions.IOSocketGet(e.Sid)
if so == nil {
- s.log.Infof("%s not emitted - WS closed (id:%s)", ExecExitEvent, e.CmdID)
+ s.Log.Infof("%s not emitted - WS closed (id:%s)", xsapiv1.ExecExitEvent, e.CmdID)
return
}
@@ -335,34 +261,34 @@ func (s *APIService) execCmd(c *gin.Context) {
// XXX - workaround to be sure that Syncthing detected all changes
if err := s.mfolders.ForceSync(prjID); err != nil {
- s.log.Errorf("Error while syncing folder %s: %v", prjID, err)
+ s.Log.Errorf("Error while syncing folder %s: %v", prjID, err)
}
if !exitImm {
// Wait end of file sync
// FIXME pass as argument
tmo := 60
for t := tmo; t > 0; t-- {
- s.log.Debugf("Wait file in-sync for %s (%d/%d)", prjID, t, tmo)
+ s.Log.Debugf("Wait file in-sync for %s (%d/%d)", prjID, t, tmo)
if sync, err := s.mfolders.IsFolderInSync(prjID); sync || err != nil {
if err != nil {
- s.log.Errorf("ERROR IsFolderInSync (%s): %v", prjID, err)
+ s.Log.Errorf("ERROR IsFolderInSync (%s): %v", prjID, err)
}
break
}
time.Sleep(time.Second)
}
- s.log.Debugf("OK file are synchronized.")
+ s.Log.Debugf("OK file are synchronized.")
}
// FIXME replace by .BroadcastTo a room
- errSoEmit := (*so).Emit(ExecExitEvent, ExecExitMsg{
+ errSoEmit := (*so).Emit(xsapiv1.ExecExitEvent, xsapiv1.ExecExitMsg{
CmdID: e.CmdID,
Timestamp: time.Now().String(),
Code: code,
Error: err,
})
if errSoEmit != nil {
- s.log.Errorf("WS Emit : %v", errSoEmit)
+ s.Log.Errorf("WS Emit : %v", errSoEmit)
}
}
@@ -378,7 +304,7 @@ func (s *APIService) execCmd(c *gin.Context) {
execWS.UserData = &data
// Start command execution
- s.log.Infof("Execute [Cmd ID %s]: %v %v", execWS.CmdID, execWS.Cmd, execWS.Args)
+ s.Log.Infof("Execute [Cmd ID %s]: %v %v", execWS.CmdID, execWS.Cmd, execWS.Args)
err = execWS.Start()
if err != nil {
@@ -386,19 +312,19 @@ func (s *APIService) execCmd(c *gin.Context) {
return
}
- c.JSON(http.StatusOK, ExecRes{Status: "OK", CmdID: execWS.CmdID})
+ c.JSON(http.StatusOK, xsapiv1.ExecResult{Status: "OK", CmdID: execWS.CmdID})
}
// ExecCmd executes remotely a command
func (s *APIService) execSignalCmd(c *gin.Context) {
- var args ExecSignalArgs
+ var args xsapiv1.ExecSignalArgs
if c.BindJSON(&args) != nil {
common.APIError(c, "Invalid arguments")
return
}
- s.log.Debugf("Signal %s for command ID %s", args.Signal, args.CmdID)
+ s.Log.Debugf("Signal %s for command ID %s", args.Signal, args.CmdID)
e := eows.GetEows(args.CmdID)
if e == nil {
@@ -412,5 +338,5 @@ func (s *APIService) execSignalCmd(c *gin.Context) {
return
}
- c.JSON(http.StatusOK, ExecSigRes{Status: "OK", CmdID: args.CmdID})
+ c.JSON(http.StatusOK, xsapiv1.ExecSigResult{Status: "OK", CmdID: args.CmdID})
}
diff --git a/lib/apiv1/folders.go b/lib/xdsserver/apiv1-folders.go
index 073445c..fe11e52 100644
--- a/lib/apiv1/folders.go
+++ b/lib/xdsserver/apiv1-folders.go
@@ -1,4 +1,4 @@
-package apiv1
+package xdsserver
import (
"net/http"
@@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
common "github.com/iotbzh/xds-common/golib"
- "github.com/iotbzh/xds-server/lib/folder"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
)
// getFolders returns all folders configuration
@@ -32,13 +32,13 @@ func (s *APIService) getFolder(c *gin.Context) {
// addFolder adds a new folder to server config
func (s *APIService) addFolder(c *gin.Context) {
- var cfgArg folder.FolderConfig
+ var cfgArg xsapiv1.FolderConfig
if c.BindJSON(&cfgArg) != nil {
common.APIError(c, "Invalid arguments")
return
}
- s.log.Debugln("Add folder config: ", cfgArg)
+ s.Log.Debugln("Add folder config: ", cfgArg)
newFld, err := s.mfolders.Add(cfgArg)
if err != nil {
@@ -77,7 +77,7 @@ func (s *APIService) syncFolder(c *gin.Context) {
common.APIError(c, err.Error())
return
}
- s.log.Debugln("Sync folder id: ", id)
+ s.Log.Debugln("Sync folder id: ", id)
err = s.mfolders.ForceSync(id)
if err != nil {
@@ -96,7 +96,7 @@ func (s *APIService) delFolder(c *gin.Context) {
return
}
- s.log.Debugln("Delete folder id ", id)
+ s.Log.Debugln("Delete folder id ", id)
delEntry, err := s.mfolders.Delete(id)
if err != nil {
@@ -114,9 +114,9 @@ func (s *APIService) updateFolder(c *gin.Context) {
return
}
- s.log.Debugln("Update folder id ", id)
+ s.Log.Debugln("Update folder id ", id)
- var cfgArg folder.FolderConfig
+ var cfgArg xsapiv1.FolderConfig
if c.BindJSON(&cfgArg) != nil {
common.APIError(c, "Invalid arguments")
return
diff --git a/lib/xdsserver/apiv1-make.go b/lib/xdsserver/apiv1-make.go
new file mode 100644
index 0000000..bcb4d95
--- /dev/null
+++ b/lib/xdsserver/apiv1-make.go
@@ -0,0 +1,214 @@
+package xdsserver
+
+import (
+ "github.com/gin-gonic/gin"
+ common "github.com/iotbzh/xds-common/golib"
+)
+
+/* TODO: Deprecated - should be removed
+// MakeArgs is the parameters (json format) of /make command
+type MakeArgs struct {
+ ID string `json:"id"`
+ SdkID string `json:"sdkID"` // sdk ID to use for setting env
+ CmdID string `json:"cmdID"` // command unique ID
+ Args []string `json:"args"` // args to pass to make command
+ Env []string `json:"env"`
+ RPath string `json:"rpath"` // relative path into project
+ ExitImmediate bool `json:"exitImmediate"` // when true, exit event sent immediately when command exited (IOW, don't wait file synchronization)
+ CmdTimeout int `json:"timeout"` // command completion timeout in Second
+}
+
+// MakeOutMsg Message send on each output (stdout+stderr) of make command
+type MakeOutMsg struct {
+ CmdID string `json:"cmdID"`
+ Timestamp string `json:"timestamp"`
+ Stdout string `json:"stdout"`
+ Stderr string `json:"stderr"`
+}
+
+// MakeExitMsg Message send on make command exit
+type MakeExitMsg struct {
+ CmdID string `json:"cmdID"`
+ Timestamp string `json:"timestamp"`
+ Code int `json:"code"`
+ Error error `json:"error"`
+}
+
+// MakeOutEvent Event send in WS when characters are received on stdout/stderr
+const MakeOutEvent = "make:output"
+
+// MakeExitEvent Event send in WS when command exited
+const MakeExitEvent = "make:exit"
+
+var makeCommandID = 1
+*/
+
+func (s *APIService) buildMake(c *gin.Context) {
+ common.APIError(c, "/make route is not longer supported, use /exec instead")
+
+ /*
+ var args MakeArgs
+
+ if c.BindJSON(&args) != nil {
+ common.APIError(c, "Invalid arguments")
+ return
+ }
+
+ sess := s.sessions.Get(c)
+ if sess == nil {
+ common.APIError(c, "Unknown sessions")
+ return
+ }
+ sop := sess.IOSocket
+ if sop == nil {
+ common.APIError(c, "Websocket not established")
+ return
+ }
+
+ // Allow to pass id in url (/make/:id) or as JSON argument
+ idArg := c.Param("id")
+ if idArg == "" {
+ idArg = args.ID
+ }
+ if idArg == "" {
+ common.APIError(c, "Invalid id")
+ return
+ }
+ id, err := s.mfolders.ResolveID(idArg)
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+ pf := s.mfolders.Get(id)
+ if pf == nil {
+ common.APIError(c, "Unknown id")
+ return
+ }
+ folder := *pf
+ prj := folder.GetConfig()
+
+ execTmo := args.CmdTimeout
+ if execTmo == 0 {
+ // TODO get default timeout from config.json file
+ execTmo = 24 * 60 * 60 // 1 day
+ }
+
+ // TODO merge all code below with exec.go
+
+ // Define callback for output
+ var oCB common.EmitOutputCB
+ oCB = func(sid string, cmdID string, stdout, stderr string, data *map[string]interface{}) {
+ // IO socket can be nil when disconnected
+ so := s.sessions.IOSocketGet(sid)
+ if so == nil {
+ s.log.Infof("%s not emitted: WS closed - sid: %s - msg id:%s", MakeOutEvent, sid, cmdID)
+ return
+ }
+
+ // Retrieve project ID and RootPath
+ prjID := (*data)["ID"].(string)
+ prjRootPath := (*data)["RootPath"].(string)
+
+ // Cleanup any references to internal rootpath in stdout & stderr
+ stdout = strings.Replace(stdout, prjRootPath, "", -1)
+ stderr = strings.Replace(stderr, prjRootPath, "", -1)
+
+ s.log.Debugf("%s emitted - WS sid %s - id:%d - prjID:%s", MakeOutEvent, sid, id, prjID)
+
+ // FIXME replace by .BroadcastTo a room
+ err := (*so).Emit(MakeOutEvent, MakeOutMsg{
+ CmdID: cmdID,
+ Timestamp: time.Now().String(),
+ Stdout: stdout,
+ Stderr: stderr,
+ })
+ if err != nil {
+ s.log.Errorf("WS Emit : %v", err)
+ }
+ }
+
+ // Define callback for output
+ eCB := func(sid string, cmdID string, code int, err error, data *map[string]interface{}) {
+ s.log.Debugf("Command [Cmd ID %s] exited: code %d, error: %v", cmdID, code, err)
+
+ // IO socket can be nil when disconnected
+ so := s.sessions.IOSocketGet(sid)
+ if so == nil {
+ s.log.Infof("%s not emitted - WS closed (id:%s", MakeExitEvent, cmdID)
+ return
+ }
+
+ // Retrieve project ID and RootPath
+ prjID := (*data)["ID"].(string)
+ exitImm := (*data)["ExitImmediate"].(bool)
+
+ // XXX - workaround to be sure that Syncthing detected all changes
+ if err := s.mfolders.ForceSync(prjID); err != nil {
+ s.log.Errorf("Error while syncing folder %s: %v", prjID, err)
+ }
+ if !exitImm {
+ // Wait end of file sync
+ // FIXME pass as argument
+ tmo := 60
+ for t := tmo; t > 0; t-- {
+ s.log.Debugf("Wait file insync for %s (%d/%d)", prjID, t, tmo)
+ if sync, err := s.mfolders.IsFolderInSync(prjID); sync || err != nil {
+ if err != nil {
+ s.log.Errorf("ERROR IsFolderInSync (%s): %v", prjID, err)
+ }
+ break
+ }
+ time.Sleep(time.Second)
+ }
+ }
+
+ // FIXME replace by .BroadcastTo a room
+ e := (*so).Emit(MakeExitEvent, MakeExitMsg{
+ CmdID: id,
+ Timestamp: time.Now().String(),
+ Code: code,
+ Error: err,
+ })
+ if e != nil {
+ s.log.Errorf("WS Emit : %v", e)
+ }
+ }
+
+ // Unique ID for each commands
+ if args.CmdID == "" {
+ args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(makeCommandID)
+ makeCommandID++
+ }
+ cmd := []string{}
+
+ // Retrieve env command regarding Sdk ID
+ if envCmd := s.sdks.GetEnvCmd(args.SdkID, prj.DefaultSdk); len(envCmd) > 0 {
+ cmd = append(cmd, envCmd...)
+ cmd = append(cmd, "&&")
+ }
+
+ cmd = append(cmd, "cd", folder.GetFullPath(args.RPath), "&&", "make")
+ if len(args.Args) > 0 {
+ cmd = append(cmd, args.Args...)
+ }
+
+ s.log.Debugf("Execute [Cmd ID %d]: %v", args.CmdID, cmd)
+
+ data := make(map[string]interface{})
+ data["ID"] = prj.ID
+ data["RootPath"] = prj.RootPath
+ data["ExitImmediate"] = args.ExitImmediate
+
+ err = common.ExecPipeWs(cmd, args.Env, sop, sess.ID, args.CmdID, execTmo, s.log, oCB, eCB, &data)
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ c.JSON(http.StatusOK,
+ gin.H{
+ "status": "OK",
+ "cmdID": args.CmdID,
+ })
+ */
+}
diff --git a/lib/apiv1/sdks.go b/lib/xdsserver/apiv1-sdks.go
index f67a0ef..be9fcf7 100644
--- a/lib/apiv1/sdks.go
+++ b/lib/xdsserver/apiv1-sdks.go
@@ -1,4 +1,4 @@
-package apiv1
+package xdsserver
import (
"net/http"
diff --git a/lib/xdsserver/apiv1-version.go b/lib/xdsserver/apiv1-version.go
new file mode 100644
index 0000000..2c2547c
--- /dev/null
+++ b/lib/xdsserver/apiv1-version.go
@@ -0,0 +1,20 @@
+package xdsserver
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
+)
+
+// getInfo : return various information about server
+func (s *APIService) getVersion(c *gin.Context) {
+ response := xsapiv1.Version{
+ ID: s.Config.ServerUID,
+ Version: s.Config.Version,
+ APIVersion: s.Config.APIVersion,
+ VersionGitTag: s.Config.VersionGitTag,
+ }
+
+ c.JSON(http.StatusOK, response)
+}
diff --git a/lib/apiv1/apiv1.go b/lib/xdsserver/apiv1.go
index fffed2d..1f6df9e 100644
--- a/lib/apiv1/apiv1.go
+++ b/lib/xdsserver/apiv1.go
@@ -1,36 +1,20 @@
-package apiv1
+package xdsserver
import (
- "github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
-
- "github.com/iotbzh/xds-server/lib/crosssdk"
- "github.com/iotbzh/xds-server/lib/model"
- "github.com/iotbzh/xds-server/lib/session"
- "github.com/iotbzh/xds-server/lib/xdsconfig"
)
// APIService .
type APIService struct {
- router *gin.Engine
+ *Context
apiRouter *gin.RouterGroup
- sessions *session.Sessions
- cfg *xdsconfig.Config
- mfolders *model.Folders
- sdks *crosssdk.SDKs
- log *logrus.Logger
}
-// New creates a new instance of API service
-func New(r *gin.Engine, sess *session.Sessions, cfg *xdsconfig.Config, mfolders *model.Folders, sdks *crosssdk.SDKs) *APIService {
+// NewAPIV1 creates a new instance of API service
+func NewAPIV1(ctx *Context) *APIService {
s := &APIService{
- router: r,
- sessions: sess,
- apiRouter: r.Group("/api/v1"),
- cfg: cfg,
- mfolders: mfolders,
- sdks: sdks,
- log: cfg.Log,
+ Context: ctx,
+ apiRouter: ctx.WWWServer.router.Group("/api/v1"),
}
s.apiRouter.GET("/version", s.getVersion)
diff --git a/lib/xdsserver/folder-interface.go b/lib/xdsserver/folder-interface.go
new file mode 100644
index 0000000..c2b2ada
--- /dev/null
+++ b/lib/xdsserver/folder-interface.go
@@ -0,0 +1,22 @@
+package xdsserver
+
+import "github.com/iotbzh/xds-server/lib/xsapiv1"
+
+type FolderEventCBData map[string]interface{}
+type FolderEventCB func(cfg *xsapiv1.FolderConfig, data *FolderEventCBData)
+
+// IFOLDER Folder interface
+type IFOLDER interface {
+ NewUID(suffix string) string // Get a new folder UUID
+ Add(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) // Add a new folder
+ GetConfig() xsapiv1.FolderConfig // Get folder public configuration
+ GetFullPath(dir string) string // Get folder full path
+ ConvPathCli2Svr(s string) string // Convert path from Client to Server
+ ConvPathSvr2Cli(s string) string // Convert path from Server to Client
+ Remove() error // Remove a folder
+ Update(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) // Update a new folder
+ RegisterEventChange(cb *FolderEventCB, data *FolderEventCBData) error // Request events registration (sent through WS)
+ UnRegisterEventChange() error // Un-register events
+ Sync() error // Force folder files synchronization
+ IsInSync() (bool, error) // Check if folder files are in-sync
+}
diff --git a/lib/folder/folder-pathmap.go b/lib/xdsserver/folder-pathmap.go
index c5691a3..c5318de 100644
--- a/lib/folder/folder-pathmap.go
+++ b/lib/xdsserver/folder-pathmap.go
@@ -1,4 +1,4 @@
-package folder
+package xdsserver
import (
"fmt"
@@ -8,7 +8,7 @@ import (
"strings"
common "github.com/iotbzh/xds-common/golib"
- "github.com/iotbzh/xds-server/lib/xdsconfig"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
uuid "github.com/satori/go.uuid"
)
@@ -16,16 +16,16 @@ import (
// PathMap .
type PathMap struct {
- globalConfig *xdsconfig.Config
- config FolderConfig
+ *Context
+ config xsapiv1.FolderConfig
}
// NewFolderPathMap Create a new instance of PathMap
-func NewFolderPathMap(gc *xdsconfig.Config) *PathMap {
+func NewFolderPathMap(ctx *Context) *PathMap {
f := PathMap{
- globalConfig: gc,
- config: FolderConfig{
- Status: StatusDisable,
+ Context: ctx,
+ config: xsapiv1.FolderConfig{
+ Status: xsapiv1.StatusDisable,
},
}
return &f
@@ -41,7 +41,7 @@ func (f *PathMap) NewUID(suffix string) string {
}
// Add a new folder
-func (f *PathMap) Add(cfg FolderConfig) (*FolderConfig, error) {
+func (f *PathMap) Add(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
if cfg.DataPathMap.ServerPath == "" {
return nil, fmt.Errorf("ServerPath must be set")
}
@@ -49,7 +49,7 @@ func (f *PathMap) Add(cfg FolderConfig) (*FolderConfig, error) {
// Use shareRootDir if ServerPath is a relative path
dir := cfg.DataPathMap.ServerPath
if !filepath.IsAbs(dir) {
- dir = filepath.Join(f.globalConfig.FileConf.ShareRootDir, dir)
+ dir = filepath.Join(f.Config.FileConf.ShareRootDir, dir)
}
// Sanity check
@@ -92,20 +92,20 @@ func (f *PathMap) Add(cfg FolderConfig) (*FolderConfig, error) {
}
// Write a specific message that will be check back on agent side
- msg := "Pathmap checked message written by xds-server ID: " + f.globalConfig.ServerUID + "\n"
+ msg := "Pathmap checked message written by xds-server ID: " + f.Config.ServerUID + "\n"
if n, err := fd.WriteString(msg); n != len(msg) || err != nil {
return nil, fmt.Errorf(errMsg, 5, err)
}
}
}
- f.config.Status = StatusEnable
+ f.config.Status = xsapiv1.StatusEnable
return &f.config, nil
}
// GetConfig Get public part of folder config
-func (f *PathMap) GetConfig() FolderConfig {
+func (f *PathMap) GetConfig() xsapiv1.FolderConfig {
return f.config
}
@@ -146,7 +146,7 @@ func (f *PathMap) Remove() error {
}
// Update update some fields of a folder
-func (f *PathMap) Update(cfg FolderConfig) (*FolderConfig, error) {
+func (f *PathMap) Update(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
if f.config.ID != cfg.ID {
return nil, fmt.Errorf("Invalid id")
}
@@ -155,7 +155,7 @@ func (f *PathMap) Update(cfg FolderConfig) (*FolderConfig, error) {
}
// RegisterEventChange requests registration for folder change event
-func (f *PathMap) RegisterEventChange(cb *EventCB, data *EventCBData) error {
+func (f *PathMap) RegisterEventChange(cb *FolderEventCB, data *FolderEventCBData) error {
return nil
}
diff --git a/lib/folder/folder-st-disable.go b/lib/xdsserver/folder-st-disable.go
index e936494..64b1efc 100644
--- a/lib/folder/folder-st-disable.go
+++ b/lib/xdsserver/folder-st-disable.go
@@ -1,7 +1,7 @@
-package folder
+package xdsserver
import (
- "github.com/iotbzh/xds-server/lib/xdsconfig"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
uuid "github.com/satori/go.uuid"
)
@@ -11,14 +11,14 @@ import (
// STFolderDisable .
type STFolderDisable struct {
- globalConfig *xdsconfig.Config
- config FolderConfig
+ *Context
+ config xsapiv1.FolderConfig
}
// NewFolderSTDisable Create a new instance of STFolderDisable
-func NewFolderSTDisable(gc *xdsconfig.Config) *STFolderDisable {
+func NewFolderSTDisable(ctx *Context) *STFolderDisable {
f := STFolderDisable{
- globalConfig: gc,
+ Context: ctx,
}
return &f
}
@@ -33,15 +33,15 @@ func (f *STFolderDisable) NewUID(suffix string) string {
}
// Add a new folder
-func (f *STFolderDisable) Add(cfg FolderConfig) (*FolderConfig, error) {
+func (f *STFolderDisable) Add(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
f.config = cfg
- f.config.Status = StatusDisable
+ f.config.Status = xsapiv1.StatusDisable
f.config.IsInSync = false
return &f.config, nil
}
// GetConfig Get public part of folder config
-func (f *STFolderDisable) GetConfig() FolderConfig {
+func (f *STFolderDisable) GetConfig() xsapiv1.FolderConfig {
return f.config
}
@@ -66,12 +66,12 @@ func (f *STFolderDisable) Remove() error {
}
// Update update some fields of a folder
-func (f *STFolderDisable) Update(cfg FolderConfig) (*FolderConfig, error) {
+func (f *STFolderDisable) Update(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
return nil, nil
}
// RegisterEventChange requests registration for folder change event
-func (f *STFolderDisable) RegisterEventChange(cb *EventCB, data *EventCBData) error {
+func (f *STFolderDisable) RegisterEventChange(cb *FolderEventCB, data *FolderEventCBData) error {
return nil
}
diff --git a/lib/syncthing/folder-st.go b/lib/xdsserver/folder-st.go
index 74aa4bb..04bbf76 100644
--- a/lib/syncthing/folder-st.go
+++ b/lib/xdsserver/folder-st.go
@@ -1,4 +1,4 @@
-package st
+package xdsserver
import (
"fmt"
@@ -6,8 +6,8 @@ import (
"path/filepath"
"strings"
- "github.com/iotbzh/xds-server/lib/folder"
- "github.com/iotbzh/xds-server/lib/xdsconfig"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
+ st "github.com/iotbzh/xds-server/lib/syncthing"
uuid "github.com/satori/go.uuid"
"github.com/syncthing/syncthing/lib/config"
)
@@ -16,20 +16,20 @@ import (
// STFolder .
type STFolder struct {
- globalConfig *xdsconfig.Config
- st *SyncThing
- fConfig folder.FolderConfig
+ *Context
+ st *st.SyncThing
+ fConfig xsapiv1.FolderConfig
stfConfig config.FolderConfiguration
eventIDs []int
- eventChangeCB *folder.EventCB
- eventChangeCBData *folder.EventCBData
+ eventChangeCB *FolderEventCB
+ eventChangeCBData *FolderEventCBData
}
// NewFolderST Create a new instance of STFolder
-func (s *SyncThing) NewFolderST(gc *xdsconfig.Config) *STFolder {
+func NewFolderST(ctx *Context, sthg *st.SyncThing) *STFolder {
return &STFolder{
- globalConfig: gc,
- st: s,
+ Context: ctx,
+ st: sthg,
}
}
@@ -47,7 +47,7 @@ func (f *STFolder) NewUID(suffix string) string {
}
// Add a new folder
-func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
+func (f *STFolder) Add(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
// Sanity check
if cfg.DataCloudSync.SyncThingID == "" {
@@ -56,7 +56,7 @@ func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
// rootPath should not be empty
if cfg.RootPath == "" {
- cfg.RootPath = f.globalConfig.FileConf.ShareRootDir
+ cfg.RootPath = f.Config.FileConf.ShareRootDir
}
f.fConfig = cfg
@@ -64,7 +64,7 @@ func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
// Update Syncthing folder
// (except if status is ErrorConfig)
// TODO: add cache to avoid multiple requests on startup
- if f.fConfig.Status != folder.StatusErrorConfig {
+ if f.fConfig.Status != xsapiv1.StatusErrorConfig {
id, err := f.st.FolderChange(f.fConfig)
if err != nil {
return nil, err
@@ -72,12 +72,12 @@ func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
f.stfConfig, err = f.st.FolderConfigGet(id)
if err != nil {
- f.fConfig.Status = folder.StatusErrorConfig
+ f.fConfig.Status = xsapiv1.StatusErrorConfig
return nil, err
}
// Register to events to update folder status
- for _, evName := range []string{EventStateChanged, EventFolderPaused} {
+ for _, evName := range []string{st.EventStateChanged, st.EventFolderPaused} {
evID, err := f.st.Events.Register(evName, f.cbEventState, id, nil)
if err != nil {
return nil, err
@@ -86,14 +86,14 @@ func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
}
f.fConfig.IsInSync = false // will be updated later by events
- f.fConfig.Status = folder.StatusEnable
+ f.fConfig.Status = xsapiv1.StatusEnable
}
return &f.fConfig, nil
}
// GetConfig Get public part of folder config
-func (f *STFolder) GetConfig() folder.FolderConfig {
+func (f *STFolder) GetConfig() xsapiv1.FolderConfig {
return f.fConfig
}
@@ -144,7 +144,7 @@ func (f *STFolder) Remove() error {
}
// Update update some fields of a folder
-func (f *STFolder) Update(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
+func (f *STFolder) Update(cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
if f.fConfig.ID != cfg.ID {
return nil, fmt.Errorf("Invalid id")
}
@@ -153,7 +153,7 @@ func (f *STFolder) Update(cfg folder.FolderConfig) (*folder.FolderConfig, error)
}
// RegisterEventChange requests registration for folder event change
-func (f *STFolder) RegisterEventChange(cb *folder.EventCB, data *folder.EventCBData) error {
+func (f *STFolder) RegisterEventChange(cb *FolderEventCB, data *FolderEventCBData) error {
f.eventChangeCB = cb
f.eventChangeCBData = data
return nil
@@ -182,25 +182,25 @@ func (f *STFolder) IsInSync() (bool, error) {
}
// callback use to update IsInSync status
-func (f *STFolder) cbEventState(ev Event, data *EventsCBData) {
+func (f *STFolder) cbEventState(ev st.Event, data *st.EventsCBData) {
prevSync := f.fConfig.IsInSync
prevStatus := f.fConfig.Status
switch ev.Type {
- case EventStateChanged:
+ case st.EventStateChanged:
to := ev.Data["to"]
switch to {
case "scanning", "syncing":
- f.fConfig.Status = folder.StatusSyncing
+ f.fConfig.Status = xsapiv1.StatusSyncing
case "idle":
- f.fConfig.Status = folder.StatusEnable
+ f.fConfig.Status = xsapiv1.StatusEnable
}
f.fConfig.IsInSync = (to == "idle")
- case EventFolderPaused:
- if f.fConfig.Status == folder.StatusEnable {
- f.fConfig.Status = folder.StatusPause
+ case st.EventFolderPaused:
+ if f.fConfig.Status == xsapiv1.StatusEnable {
+ f.fConfig.Status = xsapiv1.StatusPause
}
f.fConfig.IsInSync = false
}
diff --git a/lib/model/folders.go b/lib/xdsserver/folders.go
index 0e28538..e36f84c 100644
--- a/lib/model/folders.go
+++ b/lib/xdsserver/folders.go
@@ -1,4 +1,4 @@
-package model
+package xdsserver
import (
"encoding/xml"
@@ -9,28 +9,25 @@ import (
"strings"
"time"
- "github.com/Sirupsen/logrus"
"github.com/franciscocpg/reflectme"
common "github.com/iotbzh/xds-common/golib"
- "github.com/iotbzh/xds-server/lib/folder"
- "github.com/iotbzh/xds-server/lib/syncthing"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
"github.com/iotbzh/xds-server/lib/xdsconfig"
"github.com/syncthing/syncthing/lib/sync"
)
// Folders Represent a an XDS folders
type Folders struct {
+ *Context
fileOnDisk string
- Conf *xdsconfig.Config
- Log *logrus.Logger
- SThg *st.SyncThing
- folders map[string]*folder.IFOLDER
+ folders map[string]*IFOLDER
registerCB []RegisteredCB
}
+// RegisteredCB Hold registered callbacks
type RegisteredCB struct {
- cb *folder.EventCB
- data *folder.EventCBData
+ cb *FolderEventCB
+ data *FolderEventCBData
}
// Mutex to make add/delete atomic
@@ -38,25 +35,23 @@ var fcMutex = sync.NewMutex()
var ffMutex = sync.NewMutex()
// FoldersNew Create a new instance of Model Folders
-func FoldersNew(cfg *xdsconfig.Config, st *st.SyncThing) *Folders {
+func FoldersNew(ctx *Context) *Folders {
file, _ := xdsconfig.FoldersConfigFilenameGet()
return &Folders{
+ Context: ctx,
fileOnDisk: file,
- Conf: cfg,
- Log: cfg.Log,
- SThg: st,
- folders: make(map[string]*folder.IFOLDER),
+ folders: make(map[string]*IFOLDER),
registerCB: []RegisteredCB{},
}
}
// LoadConfig Load folders configuration from disk
func (f *Folders) LoadConfig() error {
- var flds []folder.FolderConfig
- var stFlds []folder.FolderConfig
+ var flds []xsapiv1.FolderConfig
+ var stFlds []xsapiv1.FolderConfig
// load from disk
- if f.Conf.Options.NoFolderConfig {
+ if f.Config.Options.NoFolderConfig {
f.Log.Infof("Don't read folder config file (-no-folderconfig option is set)")
} else if f.fileOnDisk != "" {
f.Log.Infof("Use folder config file: %s", f.fileOnDisk)
@@ -90,8 +85,8 @@ func (f *Folders) LoadConfig() error {
if xf.ID == stf.ID {
found = true
// sanity check
- if xf.Type != folder.TypeCloudSync {
- flds[i].Status = folder.StatusErrorConfig
+ if xf.Type != xsapiv1.TypeCloudSync {
+ flds[i].Status = xsapiv1.StatusErrorConfig
}
break
}
@@ -107,7 +102,7 @@ func (f *Folders) LoadConfig() error {
if f.SThg != nil {
for i, xf := range flds {
// only for syncthing project
- if xf.Type != folder.TypeCloudSync {
+ if xf.Type != xsapiv1.TypeCloudSync {
continue
}
found := false
@@ -118,7 +113,7 @@ func (f *Folders) LoadConfig() error {
}
}
if !found {
- flds[i].Status = folder.StatusErrorConfig
+ flds[i].Status = xsapiv1.StatusErrorConfig
}
}
}
@@ -169,7 +164,7 @@ func (f *Folders) ResolveID(id string) (string, error) {
}
// Get returns the folder config or nil if not existing
-func (f *Folders) Get(id string) *folder.IFOLDER {
+func (f *Folders) Get(id string) *IFOLDER {
if id == "" {
return nil
}
@@ -181,7 +176,7 @@ func (f *Folders) Get(id string) *folder.IFOLDER {
}
// GetConfigArr returns the config of all folders as an array
-func (f *Folders) GetConfigArr() []folder.FolderConfig {
+func (f *Folders) GetConfigArr() []xsapiv1.FolderConfig {
fcMutex.Lock()
defer fcMutex.Unlock()
@@ -189,8 +184,8 @@ func (f *Folders) GetConfigArr() []folder.FolderConfig {
}
// getConfigArrUnsafe Same as GetConfigArr without mutex protection
-func (f *Folders) getConfigArrUnsafe() []folder.FolderConfig {
- conf := []folder.FolderConfig{}
+func (f *Folders) getConfigArrUnsafe() []xsapiv1.FolderConfig {
+ conf := []xsapiv1.FolderConfig{}
for _, v := range f.folders {
conf = append(conf, (*v).GetConfig())
}
@@ -198,12 +193,12 @@ func (f *Folders) getConfigArrUnsafe() []folder.FolderConfig {
}
// Add adds a new folder
-func (f *Folders) Add(newF folder.FolderConfig) (*folder.FolderConfig, error) {
+func (f *Folders) Add(newF xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
return f.createUpdate(newF, true, false)
}
// CreateUpdate creates or update a folder
-func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bool) (*folder.FolderConfig, error) {
+func (f *Folders) createUpdate(newF xsapiv1.FolderConfig, create bool, initial bool) (*xsapiv1.FolderConfig, error) {
fcMutex.Lock()
defer fcMutex.Unlock()
@@ -217,20 +212,20 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bo
}
// Create a new folder object
- var fld folder.IFOLDER
+ var fld IFOLDER
switch newF.Type {
// SYNCTHING
- case folder.TypeCloudSync:
+ case xsapiv1.TypeCloudSync:
if f.SThg != nil {
- fld = f.SThg.NewFolderST(f.Conf)
+ fld = NewFolderST(f.Context, f.SThg)
} else {
f.Log.Debugf("Disable project %v (syncthing not initialized)", newF.ID)
- fld = folder.NewFolderSTDisable(f.Conf)
+ fld = NewFolderSTDisable(f.Context)
}
// PATH MAP
- case folder.TypePathMap:
- fld = folder.NewFolderPathMap(f.Conf)
+ case xsapiv1.TypePathMap:
+ fld = NewFolderPathMap(f.Context)
default:
return nil, fmt.Errorf("Unsupported folder type")
}
@@ -245,7 +240,7 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bo
// Set default value if needed
if newF.Status == "" {
- newF.Status = folder.StatusDisable
+ newF.Status = xsapiv1.StatusDisable
}
if newF.Label == "" {
newF.Label = filepath.Base(newF.ClientPath)
@@ -260,7 +255,7 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bo
// Add new folder
newFolder, err := fld.Add(newF)
if err != nil {
- newF.Status = folder.StatusErrorConfig
+ newF.Status = xsapiv1.StatusErrorConfig
log.Printf("ERROR Adding folder: %v\n", err)
return newFolder, err
}
@@ -293,13 +288,13 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bo
}
// Delete deletes a specific folder
-func (f *Folders) Delete(id string) (folder.FolderConfig, error) {
+func (f *Folders) Delete(id string) (xsapiv1.FolderConfig, error) {
var err error
fcMutex.Lock()
defer fcMutex.Unlock()
- fld := folder.FolderConfig{}
+ fld := xsapiv1.FolderConfig{}
fc, exist := f.folders[id]
if !exist {
return fld, fmt.Errorf("unknown id")
@@ -320,7 +315,7 @@ func (f *Folders) Delete(id string) (folder.FolderConfig, error) {
}
// Update Update a specific folder
-func (f *Folders) Update(id string, cfg folder.FolderConfig) (*folder.FolderConfig, error) {
+func (f *Folders) Update(id string, cfg xsapiv1.FolderConfig) (*xsapiv1.FolderConfig, error) {
fcMutex.Lock()
defer fcMutex.Unlock()
@@ -330,12 +325,12 @@ func (f *Folders) Update(id string, cfg folder.FolderConfig) (*folder.FolderConf
}
// Copy current in a new object to change nothing in case of an error rises
- newCfg := folder.FolderConfig{}
+ newCfg := xsapiv1.FolderConfig{}
reflectme.Copy((*fc).GetConfig(), &newCfg)
// Only update some fields
dirty := false
- for _, fieldName := range folder.FolderConfigUpdatableFields {
+ for _, fieldName := range xsapiv1.FolderConfigUpdatableFields {
valNew, err := reflectme.GetField(cfg, fieldName)
if err == nil {
valCur, err := reflectme.GetField(newCfg, fieldName)
@@ -368,9 +363,9 @@ func (f *Folders) Update(id string, cfg folder.FolderConfig) (*folder.FolderConf
}
// RegisterEventChange requests registration for folder event change
-func (f *Folders) RegisterEventChange(id string, cb *folder.EventCB, data *folder.EventCBData) error {
+func (f *Folders) RegisterEventChange(id string, cb *FolderEventCB, data *FolderEventCBData) error {
- flds := make(map[string]*folder.IFOLDER)
+ flds := make(map[string]*IFOLDER)
if id != "" {
// Register to a specific folder
flds[id] = f.Get(id)
@@ -413,13 +408,13 @@ func (f *Folders) IsFolderInSync(id string) (bool, error) {
// Use XML format and not json to be able to save/load all fields including
// ones that are masked in json (IOW defined with `json:"-"`)
type xmlFolders struct {
- XMLName xml.Name `xml:"folders"`
- Version string `xml:"version,attr"`
- Folders []folder.FolderConfig `xml:"folders"`
+ XMLName xml.Name `xml:"folders"`
+ Version string `xml:"version,attr"`
+ Folders []xsapiv1.FolderConfig `xml:"folders"`
}
// foldersConfigRead reads folders config from disk
-func foldersConfigRead(file string, folders *[]folder.FolderConfig) error {
+func foldersConfigRead(file string, folders *[]xsapiv1.FolderConfig) error {
if !common.Exists(file) {
return fmt.Errorf("No folder config file found (%s)", file)
}
@@ -442,7 +437,7 @@ func foldersConfigRead(file string, folders *[]folder.FolderConfig) error {
}
// foldersConfigWrite writes folders config on disk
-func foldersConfigWrite(file string, folders []folder.FolderConfig) error {
+func foldersConfigWrite(file string, folders []xsapiv1.FolderConfig) error {
ffMutex.Lock()
defer ffMutex.Unlock()
diff --git a/lib/xdsserver/sdk.go b/lib/xdsserver/sdk.go
new file mode 100644
index 0000000..c0acb24
--- /dev/null
+++ b/lib/xdsserver/sdk.go
@@ -0,0 +1,56 @@
+package xdsserver
+
+import (
+ "fmt"
+ "path/filepath"
+
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
+ uuid "github.com/satori/go.uuid"
+)
+
+// CrossSDK Hold SDK config
+type CrossSDK struct {
+ sdk xsapiv1.SDK
+}
+
+// NewCrossSDK creates a new instance of Syncthing
+func NewCrossSDK(path string) (*CrossSDK, error) {
+ // Assume that we have .../<profile>/<version>/<arch>
+ s := CrossSDK{
+ sdk: xsapiv1.SDK{Path: path},
+ }
+
+ s.sdk.Arch = filepath.Base(path)
+
+ d := filepath.Dir(path)
+ s.sdk.Version = filepath.Base(d)
+
+ d = filepath.Dir(d)
+ s.sdk.Profile = filepath.Base(d)
+
+ // Use V3 to ensure that we get same uuid on restart
+ s.sdk.ID = uuid.NewV3(uuid.FromStringOrNil("sdks"), s.sdk.Profile+"_"+s.sdk.Arch+"_"+s.sdk.Version).String()
+ s.sdk.Name = s.sdk.Arch + " (" + s.sdk.Version + ")"
+
+ envFile := filepath.Join(path, "environment-setup*")
+ ef, err := filepath.Glob(envFile)
+ if err != nil {
+ return nil, fmt.Errorf("Cannot retrieve environment setup file: %v", err)
+ }
+ if len(ef) != 1 {
+ return nil, fmt.Errorf("No environment setup file found match %s", envFile)
+ }
+ s.sdk.EnvFile = ef[0]
+
+ return &s, nil
+}
+
+// Get Return SDK definition
+func (s *CrossSDK) Get() *xsapiv1.SDK {
+ return &s.sdk
+}
+
+// GetEnvCmd returns the command used to initialized the environment
+func (s *CrossSDK) GetEnvCmd() []string {
+ return []string{"source", s.sdk.EnvFile}
+}
diff --git a/lib/crosssdk/sdks.go b/lib/xdsserver/sdks.go
index a3da184..1a40ab5 100644
--- a/lib/crosssdk/sdks.go
+++ b/lib/xdsserver/sdks.go
@@ -1,4 +1,4 @@
-package crosssdk
+package xdsserver
import (
"fmt"
@@ -7,33 +7,34 @@ import (
"strings"
"sync"
- "github.com/Sirupsen/logrus"
common "github.com/iotbzh/xds-common/golib"
- "github.com/iotbzh/xds-server/lib/xdsconfig"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
)
// SDKs List of installed SDK
type SDKs struct {
- Sdks map[string]*SDK
+ *Context
+ Sdks map[string]*CrossSDK
mutex sync.Mutex
}
-// Init creates a new instance of Syncthing
-func Init(cfg *xdsconfig.Config, log *logrus.Logger) (*SDKs, error) {
+// NewSDKs creates a new instance of SDKs
+func NewSDKs(ctx *Context) (*SDKs, error) {
s := SDKs{
- Sdks: make(map[string]*SDK),
+ Context: ctx,
+ Sdks: make(map[string]*CrossSDK),
}
// Retrieve installed sdks
- sdkRD := cfg.FileConf.SdkRootDir
+ sdkRD := ctx.Config.FileConf.SdkRootDir
if common.Exists(sdkRD) {
// Assume that SDK install tree is <rootdir>/<profile>/<version>/<arch>
dirs, err := filepath.Glob(path.Join(sdkRD, "*", "*", "*"))
if err != nil {
- log.Debugf("Error while retrieving SDKs: dir=%s, error=%s", sdkRD, err.Error())
+ ctx.Log.Debugf("Error while retrieving SDKs: dir=%s, error=%s", sdkRD, err.Error())
return &s, err
}
s.mutex.Lock()
@@ -43,16 +44,16 @@ func Init(cfg *xdsconfig.Config, log *logrus.Logger) (*SDKs, error) {
if !common.IsDir(d) {
continue
}
- sdk, err := NewCrossSDK(d)
+ cSdk, err := NewCrossSDK(d)
if err != nil {
- log.Debugf("Error while processing SDK dir=%s, err=%s", d, err.Error())
+ ctx.Log.Debugf("Error while processing SDK dir=%s, err=%s", d, err.Error())
continue
}
- s.Sdks[sdk.ID] = sdk
+ s.Sdks[cSdk.sdk.ID] = cSdk
}
}
- log.Debugf("SDKs: %d cross sdks found", len(s.Sdks))
+ ctx.Log.Debugf("SDKs: %d cross sdks found", len(s.Sdks))
return &s, nil
}
@@ -79,7 +80,7 @@ func (s *SDKs) ResolveID(id string) (string, error) {
}
// Get returns an SDK from id
-func (s *SDKs) Get(id string) *SDK {
+func (s *SDKs) Get(id string) *xsapiv1.SDK {
s.mutex.Lock()
defer s.mutex.Unlock()
@@ -87,16 +88,16 @@ func (s *SDKs) Get(id string) *SDK {
if !exist {
return nil
}
- return sc
+ return (*sc).Get()
}
// GetAll returns all existing SDKs
-func (s *SDKs) GetAll() []SDK {
+func (s *SDKs) GetAll() []xsapiv1.SDK {
s.mutex.Lock()
defer s.mutex.Unlock()
- res := []SDK{}
+ res := []xsapiv1.SDK{}
for _, v := range s.Sdks {
- res = append(res, *v)
+ res = append(res, *(*v).Get())
}
return res
}
diff --git a/lib/session/session.go b/lib/xdsserver/sessions.go
index 60b7b8a..6da9fd8 100644
--- a/lib/session/session.go
+++ b/lib/xdsserver/sessions.go
@@ -1,4 +1,4 @@
-package session
+package xdsserver
import (
"encoding/base64"
@@ -36,7 +36,7 @@ type ClientSession struct {
// Sessions holds client sessions
type Sessions struct {
- router *gin.Engine
+ *Context
cookieMaxAge int64
sessMap map[string]ClientSession
mutex sync.Mutex
@@ -46,21 +46,19 @@ type Sessions struct {
}
// NewClientSessions .
-func NewClientSessions(router *gin.Engine, log *logrus.Logger, cookieMaxAge string, sillyLog bool) *Sessions {
+func NewClientSessions(ctx *Context, cookieMaxAge string) *Sessions {
ckMaxAge, err := strconv.ParseInt(cookieMaxAge, 10, 0)
if err != nil {
ckMaxAge = 0
}
s := Sessions{
- router: router,
- cookieMaxAge: ckMaxAge,
- sessMap: make(map[string]ClientSession),
- mutex: sync.NewMutex(),
- log: log,
- LogLevelSilly: sillyLog,
- stop: make(chan struct{}),
+ Context: ctx,
+ cookieMaxAge: ckMaxAge,
+ sessMap: make(map[string]ClientSession),
+ mutex: sync.NewMutex(),
+ stop: make(chan struct{}),
}
- s.router.Use(s.Middleware())
+ s.WWWServer.router.Use(s.Middleware())
// Start monitoring of sessions Map (use to manage expiration and cleanup)
go s.monitorSessMap()
diff --git a/lib/webserver/server.go b/lib/xdsserver/webserver.go
index 85a2c40..0e1676a 100644
--- a/lib/webserver/server.go
+++ b/lib/xdsserver/webserver.go
@@ -1,4 +1,4 @@
-package webserver
+package xdsserver
import (
"fmt"
@@ -12,62 +12,45 @@ import (
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/googollee/go-socket.io"
- "github.com/iotbzh/xds-server/lib/apiv1"
- "github.com/iotbzh/xds-server/lib/crosssdk"
- "github.com/iotbzh/xds-server/lib/model"
- "github.com/iotbzh/xds-server/lib/session"
- "github.com/iotbzh/xds-server/lib/xdsconfig"
)
-// Server .
-type Server struct {
+// WebServer .
+type WebServer struct {
+ *Context
router *gin.Engine
- api *apiv1.APIService
+ api *APIService
sIOServer *socketio.Server
webApp *gin.RouterGroup
- cfg *xdsconfig.Config
- sessions *session.Sessions
- mfolders *model.Folders
- sdks *crosssdk.SDKs
- log *logrus.Logger
- sillyLog bool
stop chan struct{} // signals intentional stop
}
const indexFilename = "index.html"
-const cookieMaxAge = "3600"
-// New creates an instance of Server
-func New(cfg *xdsconfig.Config, mfolders *model.Folders, sdks *crosssdk.SDKs, logr *logrus.Logger, sillyLog bool) *Server {
+// NewWebServer creates an instance of WebServer
+func NewWebServer(ctx *Context) *WebServer {
// Setup logging for gin router
- if logr.Level == logrus.DebugLevel {
+ if ctx.Log.Level == logrus.DebugLevel {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
// Redirect gin logs into another logger (LogVerboseOut may be stderr or a file)
- gin.DefaultWriter = cfg.LogVerboseOut
- gin.DefaultErrorWriter = cfg.LogVerboseOut
- log.SetOutput(cfg.LogVerboseOut)
+ gin.DefaultWriter = ctx.Config.LogVerboseOut
+ gin.DefaultErrorWriter = ctx.Config.LogVerboseOut
+ log.SetOutput(ctx.Config.LogVerboseOut)
// FIXME - fix pb about isTerminal=false when out is in VSC Debug Console
// Creates gin router
r := gin.New()
- svr := &Server{
+ svr := &WebServer{
router: r,
api: nil,
sIOServer: nil,
webApp: nil,
- cfg: cfg,
- sessions: nil,
- mfolders: mfolders,
- sdks: sdks,
- log: logr,
- sillyLog: sillyLog,
stop: make(chan struct{}),
}
@@ -75,7 +58,7 @@ func New(cfg *xdsconfig.Config, mfolders *model.Folders, sdks *crosssdk.SDKs, lo
}
// Serve starts a new instance of the Web Server
-func (s *Server) Serve() error {
+func (s *WebServer) Serve() error {
var err error
// Setup middlewares
@@ -84,16 +67,13 @@ func (s *Server) Serve() error {
s.router.Use(s.middlewareXDSDetails())
s.router.Use(s.middlewareCORS())
- // Sessions manager
- s.sessions = session.NewClientSessions(s.router, s.log, cookieMaxAge, s.sillyLog)
-
// Create REST API
- s.api = apiv1.New(s.router, s.sessions, s.cfg, s.mfolders, s.sdks)
+ s.api = NewAPIV1(s.Context)
// Websocket routes
s.sIOServer, err = socketio.NewServer(nil)
if err != nil {
- s.log.Fatalln(err)
+ s.Log.Fatalln(err)
}
s.router.GET("/socket.io/", s.socketHandler)
@@ -104,12 +84,12 @@ func (s *Server) Serve() error {
*/
// Web Application (serve on / )
- idxFile := path.Join(s.cfg.FileConf.WebAppDir, indexFilename)
+ idxFile := path.Join(s.Config.FileConf.WebAppDir, indexFilename)
if _, err := os.Stat(idxFile); err != nil {
- s.log.Fatalln("Web app directory not found, check/use webAppDir setting in config file: ", idxFile)
+ s.Log.Fatalln("Web app directory not found, check/use webAppDir setting in config file: ", idxFile)
}
- s.log.Infof("Serve WEB app dir: %s", s.cfg.FileConf.WebAppDir)
- s.router.Use(static.Serve("/", static.LocalFile(s.cfg.FileConf.WebAppDir, true)))
+ s.Log.Infof("Serve WEB app dir: %s", s.Config.FileConf.WebAppDir)
+ s.router.Use(static.Serve("/", static.LocalFile(s.Config.FileConf.WebAppDir, true)))
s.webApp = s.router.Group("/", s.serveIndexFile)
{
s.webApp.GET("/")
@@ -118,10 +98,10 @@ func (s *Server) Serve() error {
// Serve in the background
serveError := make(chan error, 1)
go func() {
- msg := fmt.Sprintf("Web Server running on localhost:%s ...\n", s.cfg.FileConf.HTTPPort)
- s.log.Infof(msg)
+ msg := fmt.Sprintf("Web Server running on localhost:%s ...\n", s.Config.FileConf.HTTPPort)
+ s.Log.Infof(msg)
fmt.Printf(msg)
- serveError <- http.ListenAndServe(":"+s.cfg.FileConf.HTTPPort, s.router)
+ serveError <- http.ListenAndServe(":"+s.Config.FileConf.HTTPPort, s.router)
}()
// Wait for stop, restart or error signals
@@ -129,36 +109,36 @@ func (s *Server) Serve() error {
case <-s.stop:
// Shutting down permanently
s.sessions.Stop()
- s.log.Infoln("shutting down (stop)")
+ s.Log.Infoln("shutting down (stop)")
case err = <-serveError:
// Error due to listen/serve failure
- s.log.Errorln(err)
+ s.Log.Errorln(err)
}
return nil
}
// Stop web server
-func (s *Server) Stop() {
+func (s *WebServer) Stop() {
close(s.stop)
}
// serveIndexFile provides initial file (eg. index.html) of webapp
-func (s *Server) serveIndexFile(c *gin.Context) {
+func (s *WebServer) serveIndexFile(c *gin.Context) {
c.HTML(200, indexFilename, gin.H{})
}
// Add details in Header
-func (s *Server) middlewareXDSDetails() gin.HandlerFunc {
+func (s *WebServer) middlewareXDSDetails() gin.HandlerFunc {
return func(c *gin.Context) {
- c.Header("XDS-Version", s.cfg.Version)
- c.Header("XDS-API-Version", s.cfg.APIVersion)
+ c.Header("XDS-Version", s.Config.Version)
+ c.Header("XDS-API-Version", s.Config.APIVersion)
c.Next()
}
}
// CORS middleware
-func (s *Server) middlewareCORS() gin.HandlerFunc {
+func (s *WebServer) middlewareCORS() gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Method == "OPTIONS" {
c.Header("Access-Control-Allow-Origin", "*")
@@ -174,7 +154,7 @@ func (s *Server) middlewareCORS() gin.HandlerFunc {
}
// socketHandler is the handler for the "main" websocket connection
-func (s *Server) socketHandler(c *gin.Context) {
+func (s *WebServer) socketHandler(c *gin.Context) {
// Retrieve user session
sess := s.sessions.Get(c)
@@ -184,17 +164,17 @@ func (s *Server) socketHandler(c *gin.Context) {
}
s.sIOServer.On("connection", func(so socketio.Socket) {
- s.log.Debugf("WS Connected (SID=%v)", so.Id())
+ s.Log.Debugf("WS Connected (SID=%v)", so.Id())
s.sessions.UpdateIOSocket(sess.ID, &so)
so.On("disconnection", func() {
- s.log.Debugf("WS disconnected (SID=%v)", so.Id())
+ s.Log.Debugf("WS disconnected (SID=%v)", so.Id())
s.sessions.UpdateIOSocket(sess.ID, nil)
})
})
s.sIOServer.On("error", func(so socketio.Socket, err error) {
- s.log.Errorf("WS SID=%v Error : %v", so.Id(), err.Error())
+ s.Log.Errorf("WS SID=%v Error : %v", so.Id(), err.Error())
})
s.sIOServer.ServeHTTP(c.Writer, c.Request)
diff --git a/lib/xdsserver/xdsserver.go b/lib/xdsserver/xdsserver.go
new file mode 100644
index 0000000..dcdedc4
--- /dev/null
+++ b/lib/xdsserver/xdsserver.go
@@ -0,0 +1,202 @@
+package xdsserver
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "os/signal"
+ "path/filepath"
+ "syscall"
+ "time"
+
+ "github.com/Sirupsen/logrus"
+ "github.com/codegangsta/cli"
+ "github.com/iotbzh/xds-server/lib/xsapiv1"
+
+ "github.com/iotbzh/xds-server/lib/syncthing"
+ "github.com/iotbzh/xds-server/lib/xdsconfig"
+)
+
+const cookieMaxAge = "3600"
+
+// Context holds the XDS server context
+type Context struct {
+ ProgName string
+ Cli *cli.Context
+ Config *xdsconfig.Config
+ Log *logrus.Logger
+ LogLevelSilly bool
+ SThg *st.SyncThing
+ SThgCmd *exec.Cmd
+ SThgInotCmd *exec.Cmd
+ mfolders *Folders
+ sdks *SDKs
+ WWWServer *WebServer
+ sessions *Sessions
+ Exit chan os.Signal
+}
+
+// NewXdsServer Create a new instance of XDS server
+func NewXdsServer(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{}
+
+ sillyVal, sillyLog := os.LookupEnv("XDS_LOG_SILLY")
+
+ // Define default configuration
+ ctx := Context{
+ ProgName: cliCtx.App.Name,
+ Cli: cliCtx,
+ Log: log,
+ LogLevelSilly: (sillyLog && sillyVal == "1"),
+ Exit: make(chan os.Signal, 1),
+ }
+
+ // register handler on SIGTERM / exit
+ signal.Notify(ctx.Exit, os.Interrupt, syscall.SIGTERM)
+ go handlerSigTerm(&ctx)
+
+ return &ctx
+}
+
+// Run Main function called to run XDS Server
+func (ctx *Context) Run() (int, error) {
+ var err error
+
+ // Logs redirected into a file when logfile option or logsDir config is set
+ ctx.Config.LogVerboseOut = os.Stderr
+ if ctx.Config.FileConf.LogsDir != "" {
+ if ctx.Config.Options.LogFile != "stdout" {
+ logFile := ctx.Config.Options.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 int(syscall.EPERM), fmt.Errorf(msgErr)
+ }
+ ctx.Log.Out = fdL
+
+ ctx._logPrint("Logging file: %s\n", logFile)
+ }
+
+ logFileHTTPReq := filepath.Join(ctx.Config.FileConf.LogsDir, "xds-server-verbose.log")
+ 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 int(syscall.EPERM), fmt.Errorf(msgErr)
+ }
+ ctx.Config.LogVerboseOut = fdLH
+
+ ctx._logPrint("Logging file for HTTP requests: %s\n", logFileHTTPReq)
+ }
+
+ // 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 -4, err
+ }
+ ctx._logPrint("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 -4, err
+ }
+ ctx._logPrint("Syncthing-inotify started (PID %d)\n", ctx.SThgInotCmd.Process.Pid)
+
+ // Establish connection with local Syncthing (retry if connection fail)
+ ctx._logPrint("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 -4, err
+ }
+
+ // FIXME: do we still need Builder notion ? if no cleanup
+ if ctx.Config.Builder, err = xdsconfig.NewBuilderConfig(ctx.SThg.MyID); err != nil {
+ return -4, err
+ }
+ ctx.Config.SupportedSharing[xsapiv1.TypeCloudSync] = true
+ }
+
+ // Init model folder
+ ctx.mfolders = FoldersNew(ctx)
+
+ // Load initial folders config from disk
+ if err := ctx.mfolders.LoadConfig(); err != nil {
+ return -5, err
+ }
+
+ // Init cross SDKs
+ ctx.sdks, err = NewSDKs(ctx)
+ if err != nil {
+ return -6, err
+ }
+
+ // Create Web Server
+ ctx.WWWServer = NewWebServer(ctx)
+
+ // Sessions manager
+ ctx.sessions = NewClientSessions(ctx, cookieMaxAge)
+
+ // Run Web Server until exit requested (blocking call)
+ if err = ctx.WWWServer.Serve(); err != nil {
+ ctx.Log.Println(err)
+ return -7, err
+ }
+
+ return -99, fmt.Errorf("Program exited ")
+}
+
+// Helper function to log message on both stdout and logger
+func (ctx *Context) _logPrint(format string, args ...interface{}) {
+ fmt.Printf(format, args...)
+ if ctx.Log.Out != os.Stdout {
+ ctx.Log.Infof(format, args...)
+ }
+}
+
+// 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)
+}
diff --git a/lib/xsapiv1/config.go b/lib/xsapiv1/config.go
new file mode 100644
index 0000000..33bc116
--- /dev/null
+++ b/lib/xsapiv1/config.go
@@ -0,0 +1,18 @@
+package xsapiv1
+
+// APIConfig parameters (json format) of /config command
+type APIConfig struct {
+ ServerUID string `json:"id"`
+ Version string `json:"version"`
+ APIVersion string `json:"apiVersion"`
+ VersionGitTag string `json:"gitTag"`
+ SupportedSharing map[string]bool `json:"supportedSharing"`
+ Builder BuilderConfig `json:"builder"`
+}
+
+// BuilderConfig represents the builder container configuration
+type BuilderConfig struct {
+ IP string `json:"ip"`
+ Port string `json:"port"`
+ SyncThingID string `json:"syncThingID"`
+}
diff --git a/lib/xsapiv1/events.go b/lib/xsapiv1/events.go
new file mode 100644
index 0000000..1304b3a
--- /dev/null
+++ b/lib/xsapiv1/events.go
@@ -0,0 +1,31 @@
+package xsapiv1
+
+// EventRegisterArgs Parameters (json format) of /events/register command
+type EventRegisterArgs struct {
+ Name string `json:"name"`
+ ProjectID string `json:"filterProjectID"`
+}
+
+// EventUnRegisterArgs Parameters of /events/unregister command
+type EventUnRegisterArgs struct {
+ Name string `json:"name"`
+ ID int `json:"id"`
+}
+
+// EventMsg Message send
+type EventMsg struct {
+ Time string `json:"time"`
+ Type string `json:"type"`
+ Folder FolderConfig `json:"folder"`
+}
+
+// EventEvent Event send in WS when an internal event (eg. Syncthing event is received)
+const (
+ // EventTypePrefix Used as event prefix
+ EventTypePrefix = "event:" // following by event type
+
+ // Supported Events type
+ EVTAll = EventTypePrefix + "all"
+ EVTFolderChange = EventTypePrefix + "folder-change" // type EventMsg with Data type xsapiv1.???
+ EVTFolderStateChange = EventTypePrefix + "folder-state-change" // type EventMsg with Data type xsapiv1.???
+)
diff --git a/lib/xsapiv1/exec.go b/lib/xsapiv1/exec.go
new file mode 100644
index 0000000..dce9eff
--- /dev/null
+++ b/lib/xsapiv1/exec.go
@@ -0,0 +1,76 @@
+package xsapiv1
+
+type (
+ // ExecArgs JSON parameters of /exec command
+ ExecArgs struct {
+ ID string `json:"id" binding:"required"`
+ SdkID string `json:"sdkID"` // sdk ID to use for setting env
+ CmdID string `json:"cmdID"` // command unique ID
+ Cmd string `json:"cmd" binding:"required"`
+ Args []string `json:"args"`
+ Env []string `json:"env"`
+ RPath string `json:"rpath"` // relative path into project
+ TTY bool `json:"tty"` // Use a tty, specific to gdb --tty option
+ TTYGdbserverFix bool `json:"ttyGdbserverFix"` // Set to true to activate gdbserver workaround about inferior output
+ ExitImmediate bool `json:"exitImmediate"` // when true, exit event sent immediately when command exited (IOW, don't wait file synchronization)
+ CmdTimeout int `json:"timeout"` // command completion timeout in Second
+ }
+
+ // ExecResult JSON result of /exec command
+ ExecResult struct {
+ Status string `json:"status"` // status OK
+ CmdID string `json:"cmdID"` // command unique ID
+ }
+
+ // ExecSigResult JSON result of /signal command
+ ExecSigResult struct {
+ Status string `json:"status"` // status OK
+ CmdID string `json:"cmdID"` // command unique ID
+ }
+
+ // ExecInMsg Message used to received input characters (stdin)
+ ExecInMsg struct {
+ CmdID string `json:"cmdID"`
+ Timestamp string `json:"timestamp"`
+ Stdin string `json:"stdin"`
+ }
+
+ // ExecOutMsg Message used to send output characters (stdout+stderr)
+ ExecOutMsg struct {
+ CmdID string `json:"cmdID"`
+ Timestamp string `json:"timestamp"`
+ Stdout string `json:"stdout"`
+ Stderr string `json:"stderr"`
+ }
+
+ // ExecExitMsg Message sent when executed command exited
+ ExecExitMsg struct {
+ CmdID string `json:"cmdID"`
+ Timestamp string `json:"timestamp"`
+ Code int `json:"code"`
+ Error error `json:"error"`
+ }
+
+ // ExecSignalArgs JSON parameters of /exec/signal command
+ ExecSignalArgs struct {
+ CmdID string `json:"cmdID" binding:"required"` // command id
+ Signal string `json:"signal" binding:"required"` // signal number
+ }
+)
+
+const (
+ // ExecInEvent Event send in WS when characters are sent (stdin)
+ ExecInEvent = "exec:input"
+
+ // ExecOutEvent Event send in WS when characters are received (stdout or stderr)
+ ExecOutEvent = "exec:output"
+
+ // ExecExitEvent Event send in WS when program exited
+ ExecExitEvent = "exec:exit"
+
+ // ExecInferiorInEvent Event send in WS when characters are sent to an inferior (used by gdb inferior/tty)
+ ExecInferiorInEvent = "exec:inferior-input"
+
+ // ExecInferiorOutEvent Event send in WS when characters are received by an inferior
+ ExecInferiorOutEvent = "exec:inferior-output"
+)
diff --git a/lib/folder/folder-interface.go b/lib/xsapiv1/folders.go
index 3208869..9506a1d 100644
--- a/lib/folder/folder-interface.go
+++ b/lib/xsapiv1/folders.go
@@ -1,4 +1,4 @@
-package folder
+package xsapiv1
// FolderType definition
type FolderType string
@@ -18,25 +18,6 @@ const (
StatusSyncing = "Syncing"
)
-type EventCBData map[string]interface{}
-type EventCB func(cfg *FolderConfig, data *EventCBData)
-
-// IFOLDER Folder interface
-type IFOLDER interface {
- NewUID(suffix string) string // Get a new folder UUID
- Add(cfg FolderConfig) (*FolderConfig, error) // Add a new folder
- GetConfig() FolderConfig // Get folder public configuration
- GetFullPath(dir string) string // Get folder full path
- ConvPathCli2Svr(s string) string // Convert path from Client to Server
- ConvPathSvr2Cli(s string) string // Convert path from Server to Client
- Remove() error // Remove a folder
- Update(cfg FolderConfig) (*FolderConfig, error) // Update a new folder
- RegisterEventChange(cb *EventCB, data *EventCBData) error // Request events registration (sent through WS)
- UnRegisterEventChange() error // Un-register events
- Sync() error // Force folder files synchronization
- IsInSync() (bool, error) // Check if folder files are in-sync
-}
-
// FolderConfig is the config for one folder
type FolderConfig struct {
ID string `json:"id"`
diff --git a/lib/xsapiv1/sdks.go b/lib/xsapiv1/sdks.go
new file mode 100644
index 0000000..5ca4dd5
--- /dev/null
+++ b/lib/xsapiv1/sdks.go
@@ -0,0 +1,14 @@
+package xsapiv1
+
+// SDK Define a cross tool chain used to build application
+type SDK struct {
+ ID string `json:"id" binding:"required"`
+ Name string `json:"name"`
+ Profile string `json:"profile"`
+ Version string `json:"version"`
+ Arch string `json:"arch"`
+ Path string `json:"path"`
+
+ // Not exported fields
+ EnvFile string `json:"-"`
+}
diff --git a/lib/xsapiv1/version.go b/lib/xsapiv1/version.go
new file mode 100644
index 0000000..8c3a742
--- /dev/null
+++ b/lib/xsapiv1/version.go
@@ -0,0 +1,9 @@
+package xsapiv1
+
+// XDS server Version
+type Version struct {
+ ID string `json:"id"`
+ Version string `json:"version"`
+ APIVersion string `json:"apiVersion"`
+ VersionGitTag string `json:"gitTag"`
+}
diff --git a/main.go b/main.go
index 6089e74..1bb7110 100644
--- a/main.go
+++ b/main.go
@@ -5,20 +5,11 @@ package main
import (
"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/folder"
- "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 (
@@ -39,192 +30,23 @@ var AppVersion = "?.?.?"
// Should be set by compilation -ldflags "-X main.AppSubVersion=xxx"
var AppSubVersion = "unknown-dev"
-// Context holds the XDS server context
-type Context struct {
- ProgName string
- Cli *cli.Context
- Config *xdsconfig.Config
- Log *logrus.Logger
- LogLevelSilly bool
- 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{}
-
- sillyVal, sillyLog := os.LookupEnv("XDS_LOG_SILLY")
-
- // Define default configuration
- ctx := Context{
- ProgName: cliCtx.App.Name,
- Cli: cliCtx,
- Log: log,
- LogLevelSilly: (sillyLog && sillyVal == "1"),
- 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)
-}
-
-// Helper function to log message on both stdout and logger
-func logPrint(ctx *Context, format string, args ...interface{}) {
- fmt.Printf(format, args...)
- if ctx.Log.Out != os.Stdout {
- ctx.Log.Infof(format, args...)
- }
-}
-
// XDS Server application main routine
func xdsApp(cliCtx *cli.Context) error {
var err error
// Create XDS server context
- ctx := NewContext(cliCtx)
+ ctxSvr := xdsserver.NewXdsServer(cliCtx)
// Load config
- cfg, err := xdsconfig.Init(ctx.Cli, ctx.Log)
+ ctxSvr.Config, err = xdsconfig.Init(cliCtx, ctxSvr.Log)
if err != nil {
return cli.NewExitError(err, -2)
}
- ctx.Config = cfg
- // Logs redirected into a file when logfile option or logsDir config is set
- ctx.Config.LogVerboseOut = os.Stderr
- if ctx.Config.FileConf.LogsDir != "" {
- if ctx.Config.Options.LogFile != "stdout" {
- logFile := ctx.Config.Options.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
-
- logPrint(ctx, "Logging file: %s\n", logFile)
- }
-
- logFileHTTPReq := filepath.Join(ctx.Config.FileConf.LogsDir, "xds-server-verbose.log")
- 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
-
- logPrint(ctx, "Logging file for HTTP requests: %s\n", logFileHTTPReq)
- }
-
- // 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)
- }
- logPrint(ctx, "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)
- }
- logPrint(ctx, "Syncthing-inotify started (PID %d)\n", ctx.SThgInotCmd.Process.Pid)
-
- // Establish connection with local Syncthing (retry if connection fail)
- logPrint(ctx, "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)
- }
- ctx.Config.SupportedSharing[folder.TypeCloudSync] = true
- }
-
- // 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 Web Server
- ctx.WWWServer = webserver.New(ctx.Config, ctx.MFolders, ctx.SDKs, ctx.Log, ctx.LogLevelSilly)
-
- // Run Web Server until exit requested (blocking call)
- if err = ctx.WWWServer.Serve(); err != nil {
- ctx.Log.Println(err)
- return cli.NewExitError(err, -7)
- }
+ // Run XDS Server (main loop)
+ errCode, err := ctxSvr.Run()
- return cli.NewExitError("Program exited ", -99)
+ return cli.NewExitError(err, errCode)
}
// main