summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/apiv1/agent.go70
-rw-r--r--lib/apiv1/apiv1.go11
-rw-r--r--lib/apiv1/events.go9
-rw-r--r--lib/apiv1/exec.go25
-rw-r--r--lib/apiv1/folders.go22
-rw-r--r--lib/apiv1/make.go30
-rw-r--r--lib/apiv1/sdks.go6
-rw-r--r--lib/apiv1/version.go2
-rw-r--r--lib/crosssdk/sdk.go7
-rw-r--r--lib/crosssdk/sdks.go75
-rw-r--r--lib/folder/folder-interface.go15
-rw-r--r--lib/folder/folder-pathmap.go52
-rw-r--r--lib/folder/folder-st-disable.go6
-rw-r--r--lib/model/folders.go47
-rw-r--r--lib/session/session.go32
-rw-r--r--lib/syncthing/folder-st.go10
-rw-r--r--lib/syncthing/st.go18
-rw-r--r--lib/syncthing/stfolder.go24
-rw-r--r--lib/webserver/server.go6
-rw-r--r--lib/xdsconfig/builderconfig.go5
-rw-r--r--lib/xdsconfig/config.go32
-rw-r--r--lib/xdsconfig/data.go87
-rw-r--r--lib/xdsconfig/fileconfig.go19
23 files changed, 386 insertions, 224 deletions
diff --git a/lib/apiv1/agent.go b/lib/apiv1/agent.go
deleted file mode 100644
index 925f12b..0000000
--- a/lib/apiv1/agent.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package apiv1
-
-import (
- "net/http"
- "path"
- "strings"
-
- "path/filepath"
-
- "github.com/gin-gonic/gin"
- common "github.com/iotbzh/xds-common/golib"
-)
-
-// XDSAgentTarball .
-type XDSAgentTarball struct {
- OS string `json:"os"`
- Arch string `json:"arch"`
- Version string `json:"version"`
- RawVersion string `json:"raw-version"`
- FileURL string `json:"fileUrl"`
-}
-
-// XDSAgentInfo .
-type XDSAgentInfo struct {
- Tarballs []XDSAgentTarball `json:"tarballs"`
-}
-
-// getXdsAgentInfo : return various information about Xds Agent
-func (s *APIService) getXdsAgentInfo(c *gin.Context) {
-
- res := XDSAgentInfo{}
- tarballURL := "assets/xds-agent-tarballs"
- tarballDir := filepath.Join(s.cfg.FileConf.WebAppDir, "assets", "xds-agent-tarballs")
- if common.Exists(tarballDir) {
- files, err := filepath.Glob(path.Join(tarballDir, "xds-agent_*.zip"))
- if err != nil {
- s.log.Debugf("Error while retrieving xds-agent tarballs: dir=%s, error=%v", tarballDir, err)
- }
- for _, ff := range files {
- file := filepath.Base(ff)
- // Assume that tarball name format is: xds-agent_OS-ARCH-RAWVERSION.zip
- fs := strings.TrimSuffix(strings.TrimPrefix(file, "xds-agent_"), ".zip")
- f := strings.Split(fs, "-")
-
- if len(f) >= 3 {
- vers := strings.Split(f[2], "_")
- ver := f[2]
- if len(vers) > 1 {
- ver = vers[0]
- }
-
- newT := XDSAgentTarball{
- OS: f[0],
- Arch: f[1],
- Version: ver,
- RawVersion: f[2],
- FileURL: filepath.Join(tarballURL, file),
- }
-
- s.log.Infof("Added XDS-Agent tarball: %s", file)
- res.Tarballs = append(res.Tarballs, newT)
-
- } else {
- s.log.Debugf("Error while retrieving xds-agent, decoding failure: file:%v", ff)
- }
- }
- }
-
- c.JSON(http.StatusOK, res)
-}
diff --git a/lib/apiv1/apiv1.go b/lib/apiv1/apiv1.go
index 262f513..d10a08e 100644
--- a/lib/apiv1/apiv1.go
+++ b/lib/apiv1/apiv1.go
@@ -34,19 +34,18 @@ func New(r *gin.Engine, sess *session.Sessions, cfg *xdsconfig.Config, mfolders
}
s.apiRouter.GET("/version", s.getVersion)
- s.apiRouter.GET("/xdsagent/info", s.getXdsAgentInfo)
s.apiRouter.GET("/config", s.getConfig)
s.apiRouter.POST("/config", s.setConfig)
s.apiRouter.GET("/folders", s.getFolders)
- s.apiRouter.GET("/folder/:id", s.getFolder)
- s.apiRouter.POST("/folder", s.addFolder)
- s.apiRouter.POST("/folder/sync/:id", s.syncFolder)
- s.apiRouter.DELETE("/folder/:id", s.delFolder)
+ s.apiRouter.GET("/folders/:id", s.getFolder)
+ s.apiRouter.POST("/folders", s.addFolder)
+ s.apiRouter.POST("/folders/sync/:id", s.syncFolder)
+ s.apiRouter.DELETE("/folders/:id", s.delFolder)
s.apiRouter.GET("/sdks", s.getSdks)
- s.apiRouter.GET("/sdk/:id", s.getSdk)
+ s.apiRouter.GET("/sdks/:id", s.getSdk)
s.apiRouter.POST("/make", s.buildMake)
s.apiRouter.POST("/make/:id", s.buildMake)
diff --git a/lib/apiv1/events.go b/lib/apiv1/events.go
index da8298c..9444262 100644
--- a/lib/apiv1/events.go
+++ b/lib/apiv1/events.go
@@ -112,6 +112,9 @@ func (s *APIService) eventsRegister(c *gin.Context) {
Folder: *cfg,
}
+ s.log.Debugf("WS Emit %s - Status=%10s, IsInSync=%6v, ID=%s",
+ EventEventType+evType, cfg.Status, cfg.IsInSync, cfg.ID)
+
if err := (*so).Emit(EventEventType+evType, msg); err != nil {
s.log.Errorf("WS Emit Folder StateChanged event : %v", err)
}
@@ -119,11 +122,15 @@ func (s *APIService) eventsRegister(c *gin.Context) {
data := make(folder.EventCBData)
data["sid"] = sess.ID
- err := s.mfolders.RegisterEventChange(args.ProjectID, &cbFunc, &data)
+ prjID, err := s.mfolders.ResolveID(args.ProjectID)
if err != nil {
common.APIError(c, err.Error())
return
}
+ if err = s.mfolders.RegisterEventChange(prjID, &cbFunc, &data); err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
c.JSON(http.StatusOK, gin.H{"status": "OK"})
}
diff --git a/lib/apiv1/exec.go b/lib/apiv1/exec.go
index de40c70..30444c1 100644
--- a/lib/apiv1/exec.go
+++ b/lib/apiv1/exec.go
@@ -19,7 +19,8 @@ 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
+ 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"`
@@ -104,15 +105,19 @@ func (s *APIService) execCmd(c *gin.Context) {
}
// Allow to pass id in url (/exec/:id) or as JSON argument
- id := c.Param("id")
- if id == "" {
- id = args.ID
+ idArg := c.Param("id")
+ if idArg == "" {
+ idArg = args.ID
}
- if id == "" {
+ if idArg == "" {
common.APIError(c, "Invalid id")
return
}
-
+ id, err := s.mfolders.ResolveID(idArg)
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
f := s.mfolders.Get(id)
if f == nil {
common.APIError(c, "Unknown id")
@@ -168,11 +173,13 @@ func (s *APIService) execCmd(c *gin.Context) {
}
// Unique ID for each commands
- cmdID := strconv.Itoa(execCommandID)
- execCommandID++
+ if args.CmdID == "" {
+ args.CmdID = s.cfg.ServerUID[:18] + "_" + strconv.Itoa(execCommandID)
+ execCommandID++
+ }
// Create new execution over WS context
- execWS := eows.New(strings.Join(cmd, " "), cmdArgs, sop, sess.ID, cmdID)
+ execWS := eows.New(strings.Join(cmd, " "), cmdArgs, sop, sess.ID, args.CmdID)
execWS.Log = s.log
// Append client project dir to environment
diff --git a/lib/apiv1/folders.go b/lib/apiv1/folders.go
index a231b86..398e21c 100644
--- a/lib/apiv1/folders.go
+++ b/lib/apiv1/folders.go
@@ -16,7 +16,12 @@ func (s *APIService) getFolders(c *gin.Context) {
// getFolder returns a specific folder configuration
func (s *APIService) getFolder(c *gin.Context) {
- f := s.mfolders.Get(c.Param("id"))
+ id, err := s.mfolders.ResolveID(c.Param("id"))
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+ f := s.mfolders.Get(id)
if f == nil {
common.APIError(c, "Invalid id")
return
@@ -67,11 +72,14 @@ func (s *APIService) addFolder(c *gin.Context) {
// syncFolder force synchronization of folder files
func (s *APIService) syncFolder(c *gin.Context) {
- id := c.Param("id")
-
+ id, err := s.mfolders.ResolveID(c.Param("id"))
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
s.log.Debugln("Sync folder id: ", id)
- err := s.mfolders.ForceSync(id)
+ err = s.mfolders.ForceSync(id)
if err != nil {
common.APIError(c, err.Error())
return
@@ -82,7 +90,11 @@ func (s *APIService) syncFolder(c *gin.Context) {
// delFolder deletes folder from server config
func (s *APIService) delFolder(c *gin.Context) {
- id := c.Param("id")
+ id, err := s.mfolders.ResolveID(c.Param("id"))
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
s.log.Debugln("Delete folder id ", id)
diff --git a/lib/apiv1/make.go b/lib/apiv1/make.go
index cf76476..6e0c7d6 100644
--- a/lib/apiv1/make.go
+++ b/lib/apiv1/make.go
@@ -15,7 +15,8 @@ import (
// 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
+ 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
@@ -67,15 +68,19 @@ func (s *APIService) buildMake(c *gin.Context) {
}
// Allow to pass id in url (/make/:id) or as JSON argument
- id := c.Param("id")
- if id == "" {
- id = args.ID
+ idArg := c.Param("id")
+ if idArg == "" {
+ idArg = args.ID
}
- if 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")
@@ -171,8 +176,11 @@ func (s *APIService) buildMake(c *gin.Context) {
}
}
- cmdID := strconv.Itoa(makeCommandID)
- makeCommandID++
+ // 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
@@ -186,14 +194,14 @@ func (s *APIService) buildMake(c *gin.Context) {
cmd = append(cmd, args.Args...)
}
- s.log.Debugf("Execute [Cmd ID %d]: %v", cmdID, cmd)
+ 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, cmdID, execTmo, s.log, oCB, eCB, &data)
+ 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
@@ -202,6 +210,6 @@ func (s *APIService) buildMake(c *gin.Context) {
c.JSON(http.StatusOK,
gin.H{
"status": "OK",
- "cmdID": cmdID,
+ "cmdID": args.CmdID,
})
}
diff --git a/lib/apiv1/sdks.go b/lib/apiv1/sdks.go
index 52af506..f67a0ef 100644
--- a/lib/apiv1/sdks.go
+++ b/lib/apiv1/sdks.go
@@ -2,7 +2,6 @@ package apiv1
import (
"net/http"
- "strconv"
"github.com/gin-gonic/gin"
common "github.com/iotbzh/xds-common/golib"
@@ -15,12 +14,11 @@ func (s *APIService) getSdks(c *gin.Context) {
// getSdk returns a specific Sdk configuration
func (s *APIService) getSdk(c *gin.Context) {
- id, err := strconv.Atoi(c.Param("id"))
+ id, err := s.sdks.ResolveID(c.Param("id"))
if err != nil {
- common.APIError(c, "Invalid id")
+ common.APIError(c, err.Error())
return
}
-
sdk := s.sdks.Get(id)
if sdk.Profile == "" {
common.APIError(c, "Invalid id")
diff --git a/lib/apiv1/version.go b/lib/apiv1/version.go
index e022441..8f928ec 100644
--- a/lib/apiv1/version.go
+++ b/lib/apiv1/version.go
@@ -7,6 +7,7 @@ import (
)
type version struct {
+ ID string `json:"id"`
Version string `json:"version"`
APIVersion string `json:"apiVersion"`
VersionGitTag string `json:"gitTag"`
@@ -15,6 +16,7 @@ type version struct {
// 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,
diff --git a/lib/crosssdk/sdk.go b/lib/crosssdk/sdk.go
index 5a5770d..5be8954 100644
--- a/lib/crosssdk/sdk.go
+++ b/lib/crosssdk/sdk.go
@@ -3,6 +3,8 @@ package crosssdk
import (
"fmt"
"path/filepath"
+
+ uuid "github.com/satori/go.uuid"
)
// SDK Define a cross tool chain used to build application
@@ -31,8 +33,9 @@ func NewCrossSDK(path string) (*SDK, error) {
d = filepath.Dir(d)
s.Profile = filepath.Base(d)
- s.ID = s.Profile + "_" + s.Arch + "_" + s.Version
- s.Name = s.Arch + " (" + s.Version + ")"
+ // 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)
diff --git a/lib/crosssdk/sdks.go b/lib/crosssdk/sdks.go
index 0da0d1b..e3d6607 100644
--- a/lib/crosssdk/sdks.go
+++ b/lib/crosssdk/sdks.go
@@ -1,8 +1,10 @@
package crosssdk
import (
+ "fmt"
"path"
"path/filepath"
+ "strings"
"sync"
"github.com/Sirupsen/logrus"
@@ -12,14 +14,16 @@ import (
// SDKs List of installed SDK
type SDKs struct {
- Sdks []SDK
+ Sdks map[string]*SDK
mutex sync.Mutex
}
// Init creates a new instance of Syncthing
func Init(cfg *xdsconfig.Config, log *logrus.Logger) (*SDKs, error) {
- s := SDKs{}
+ s := SDKs{
+ Sdks: make(map[string]*SDK),
+ }
// Retrieve installed sdks
sdkRD := cfg.FileConf.SdkRootDir
@@ -44,7 +48,7 @@ func Init(cfg *xdsconfig.Config, log *logrus.Logger) (*SDKs, error) {
log.Debugf("Error while processing SDK dir=%s, err=%s", d, err.Error())
continue
}
- s.Sdks = append(s.Sdks, *sdk)
+ s.Sdks[sdk.ID] = sdk
}
}
@@ -53,23 +57,50 @@ func Init(cfg *xdsconfig.Config, log *logrus.Logger) (*SDKs, error) {
return &s, nil
}
-// GetAll returns all existing SDKs
-func (s *SDKs) GetAll() []SDK {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- res := s.Sdks
- return res
+// ResolveID Complete an SDK ID (helper for user that can use partial ID value)
+func (s *SDKs) ResolveID(id string) (string, error) {
+ if id == "" {
+ return "", nil
+ }
+
+ match := []string{}
+ for iid := range s.Sdks {
+ fmt.Printf("SEB prefix iid=%v id=%v\n", iid, id)
+ if strings.HasPrefix(iid, id) {
+ match = append(match, iid)
+ fmt.Printf(" SEB match (%d): %v\n", len(match), match)
+ }
+ }
+ fmt.Printf("SEB match (%d): %v\n", len(match), match)
+
+ if len(match) == 1 {
+ return match[0], nil
+ } else if len(match) == 0 {
+ return id, fmt.Errorf("Unknown id")
+ }
+ return id, fmt.Errorf("Multiple IDs found with provided prefix: " + id)
}
// Get returns an SDK from id
-func (s *SDKs) Get(id int) SDK {
+func (s *SDKs) Get(id string) *SDK {
s.mutex.Lock()
defer s.mutex.Unlock()
- if id < 0 || id > len(s.Sdks) {
- return SDK{}
+ sc, exist := s.Sdks[id]
+ if !exist {
+ return nil
+ }
+ return sc
+}
+
+// GetAll returns all existing SDKs
+func (s *SDKs) GetAll() []SDK {
+ s.mutex.Lock()
+ defer s.mutex.Unlock()
+ res := []SDK{}
+ for _, v := range s.Sdks {
+ res = append(res, *v)
}
- res := s.Sdks[id]
return res
}
@@ -82,15 +113,15 @@ func (s *SDKs) GetEnvCmd(id string, defaultID string) []string {
s.mutex.Lock()
defer s.mutex.Unlock()
- defaultEnv := []string{}
- for _, sdk := range s.Sdks {
- if sdk.ID == id {
- return sdk.GetEnvCmd()
- }
- if sdk.ID == defaultID {
- defaultEnv = sdk.GetEnvCmd()
- }
+
+ if sdk, exist := s.Sdks[id]; exist {
+ return sdk.GetEnvCmd()
}
+
+ if sdk, exist := s.Sdks[defaultID]; defaultID != "" && exist {
+ return sdk.GetEnvCmd()
+ }
+
// Return default env that may be empty
- return defaultEnv
+ return []string{}
}
diff --git a/lib/folder/folder-interface.go b/lib/folder/folder-interface.go
index 4beccb8..9eb6829 100644
--- a/lib/folder/folder-interface.go
+++ b/lib/folder/folder-interface.go
@@ -1,12 +1,12 @@
package folder
// FolderType definition
-type FolderType int
+type FolderType string
const (
- TypePathMap = 1
- TypeCloudSync = 2
- TypeCifsSmb = 3
+ TypePathMap = "PathMap"
+ TypeCloudSync = "CloudSync"
+ TypeCifsSmb = "CIFS"
)
// Folder Status definition
@@ -61,10 +61,13 @@ type FolderConfig struct {
// PathMapConfig Path mapping specific data
type PathMapConfig struct {
ServerPath string `json:"serverPath"`
+
+ // Don't keep temporary file name (IOW we don't want to save it and reuse it)
+ CheckFile string `json:"checkFile" xml:"-"`
+ CheckContent string `json:"checkContent" xml:"-"`
}
// CloudSyncConfig CloudSync (AKA Syncthing) specific data
type CloudSyncConfig struct {
- SyncThingID string `json:"syncThingID"`
- BuilderSThgID string `json:"builderSThgID"`
+ SyncThingID string `json:"syncThingID"`
}
diff --git a/lib/folder/folder-pathmap.go b/lib/folder/folder-pathmap.go
index 1020026..e200164 100644
--- a/lib/folder/folder-pathmap.go
+++ b/lib/folder/folder-pathmap.go
@@ -24,13 +24,20 @@ type PathMap struct {
func NewFolderPathMap(gc *xdsconfig.Config) *PathMap {
f := PathMap{
globalConfig: gc,
+ config: FolderConfig{
+ Status: StatusDisable,
+ },
}
return &f
}
// NewUID Get a UUID
func (f *PathMap) NewUID(suffix string) string {
- return uuid.NewV1().String() + "_" + suffix
+ uuid := uuid.NewV1().String()
+ if len(suffix) > 0 {
+ uuid += "_" + suffix
+ }
+ return uuid
}
// Add a new folder
@@ -55,22 +62,43 @@ func (f *PathMap) Add(cfg FolderConfig) (*FolderConfig, error) {
if !common.Exists(dir) {
return nil, fmt.Errorf("ServerPath directory is not accessible: %s", dir)
}
- file, err := ioutil.TempFile(dir, "xds_pathmap_check")
- if err != nil {
- return nil, fmt.Errorf("ServerPath sanity check error: %s", err.Error())
- }
- defer os.Remove(file.Name())
-
- msg := "sanity check PathMap Add folder"
- n, err := file.Write([]byte(msg))
- if err != nil || n != len(msg) {
- return nil, fmt.Errorf("ServerPath sanity check error: %s", err.Error())
- }
f.config = cfg
f.config.RootPath = dir
f.config.DataPathMap.ServerPath = dir
f.config.IsInSync = true
+
+ // Verify file created by XDS agent when needed
+ if cfg.DataPathMap.CheckFile != "" {
+ errMsg := "ServerPath sanity check error (%d): %v"
+ ckFile := f.ConvPathCli2Svr(cfg.DataPathMap.CheckFile)
+ if !common.Exists(ckFile) {
+ return nil, fmt.Errorf(errMsg, 1, "file not present")
+ }
+ if cfg.DataPathMap.CheckContent != "" {
+ fd, err := os.OpenFile(ckFile, os.O_APPEND|os.O_RDWR, 0600)
+ if err != nil {
+ return nil, fmt.Errorf(errMsg, 2, err)
+ }
+ defer fd.Close()
+
+ // Check specific message written by agent
+ content, err := ioutil.ReadAll(fd)
+ if err != nil {
+ return nil, fmt.Errorf(errMsg, 3, err)
+ }
+ if string(content) != cfg.DataPathMap.CheckContent {
+ return nil, fmt.Errorf(errMsg, 4, "file content differ")
+ }
+
+ // 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"
+ if n, err := fd.WriteString(msg); n != len(msg) || err != nil {
+ return nil, fmt.Errorf(errMsg, 5, err)
+ }
+ }
+ }
+
f.config.Status = StatusEnable
return &f.config, nil
diff --git a/lib/folder/folder-st-disable.go b/lib/folder/folder-st-disable.go
index f90b776..7b53ca8 100644
--- a/lib/folder/folder-st-disable.go
+++ b/lib/folder/folder-st-disable.go
@@ -25,7 +25,11 @@ func NewFolderSTDisable(gc *xdsconfig.Config) *STFolderDisable {
// NewUID Get a UUID
func (f *STFolderDisable) NewUID(suffix string) string {
- return uuid.NewV1().String() + "_" + suffix
+ uuid := uuid.NewV1().String()
+ if len(suffix) > 0 {
+ uuid += "_" + suffix
+ }
+ return uuid
}
// Add a new folder
diff --git a/lib/model/folders.go b/lib/model/folders.go
index 576c4a2..b8e6cf5 100644
--- a/lib/model/folders.go
+++ b/lib/model/folders.go
@@ -146,6 +146,27 @@ func (f *Folders) SaveConfig() error {
return foldersConfigWrite(f.fileOnDisk, f.getConfigArrUnsafe())
}
+// ResolveID Complete a Folder ID (helper for user that can use partial ID value)
+func (f *Folders) ResolveID(id string) (string, error) {
+ if id == "" {
+ return "", nil
+ }
+
+ match := []string{}
+ for iid := range f.folders {
+ if strings.HasPrefix(iid, id) {
+ match = append(match, iid)
+ }
+ }
+
+ if len(match) == 1 {
+ return match[0], nil
+ } else if len(match) == 0 {
+ return id, fmt.Errorf("Unknown id")
+ }
+ return id, fmt.Errorf("Multiple IDs found with provided prefix: " + id)
+}
+
// Get returns the folder config or nil if not existing
func (f *Folders) Get(id string) *folder.IFOLDER {
if id == "" {
@@ -168,8 +189,7 @@ func (f *Folders) GetConfigArr() []folder.FolderConfig {
// getConfigArrUnsafe Same as GetConfigArr without mutex protection
func (f *Folders) getConfigArrUnsafe() []folder.FolderConfig {
- var conf []folder.FolderConfig
-
+ conf := []folder.FolderConfig{}
for _, v := range f.folders {
conf = append(conf, (*v).GetConfig())
}
@@ -214,24 +234,23 @@ func (f *Folders) createUpdate(newF folder.FolderConfig, create bool, initial bo
return nil, fmt.Errorf("Unsupported folder type")
}
+ // Allocate a new UUID
+ if create {
+ newF.ID = fld.NewUID("")
+ }
+ if !create && newF.ID == "" {
+ return nil, fmt.Errorf("Cannot update folder with null ID")
+ }
+
// Set default value if needed
if newF.Status == "" {
newF.Status = folder.StatusDisable
}
if newF.Label == "" {
- newF.Label = filepath.Base(newF.ClientPath) + "_" + newF.ID[0:8]
- }
-
- // Allocate a new UUID
- if create {
- i := len(newF.Label)
- if i > 20 {
- i = 20
+ newF.Label = filepath.Base(newF.ClientPath)
+ if len(newF.ID) > 8 {
+ newF.Label += "_" + newF.ID[0:8]
}
- newF.ID = fld.NewUID(newF.Label[:i])
- }
- if !create && newF.ID == "" {
- return nil, fmt.Errorf("Cannot update folder with null ID")
}
// Normalize path (needed for Windows path including bashlashes)
diff --git a/lib/session/session.go b/lib/session/session.go
index d4e1ad3..60b7b8a 100644
--- a/lib/session/session.go
+++ b/lib/session/session.go
@@ -36,27 +36,29 @@ type ClientSession struct {
// Sessions holds client sessions
type Sessions struct {
- router *gin.Engine
- cookieMaxAge int64
- sessMap map[string]ClientSession
- mutex sync.Mutex
- log *logrus.Logger
- stop chan struct{} // signals intentional stop
+ router *gin.Engine
+ cookieMaxAge int64
+ sessMap map[string]ClientSession
+ mutex sync.Mutex
+ log *logrus.Logger
+ LogLevelSilly bool
+ stop chan struct{} // signals intentional stop
}
// NewClientSessions .
-func NewClientSessions(router *gin.Engine, log *logrus.Logger, cookieMaxAge string) *Sessions {
+func NewClientSessions(router *gin.Engine, log *logrus.Logger, cookieMaxAge string, sillyLog bool) *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,
- stop: make(chan struct{}),
+ router: router,
+ cookieMaxAge: ckMaxAge,
+ sessMap: make(map[string]ClientSession),
+ mutex: sync.NewMutex(),
+ log: log,
+ LogLevelSilly: sillyLog,
+ stop: make(chan struct{}),
}
s.router.Use(s.Middleware())
@@ -197,15 +199,13 @@ func (s *Sessions) refresh(sid string) {
}
func (s *Sessions) monitorSessMap() {
- const dbgFullTrace = false // for debugging
-
for {
select {
case <-s.stop:
s.log.Debugln("Stop monitorSessMap")
return
case <-time.After(sessionMonitorTime * time.Second):
- if dbgFullTrace {
+ if s.LogLevelSilly {
s.log.Debugf("Sessions Map size: %d", len(s.sessMap))
s.log.Debugf("Sessions Map : %v", s.sessMap)
}
diff --git a/lib/syncthing/folder-st.go b/lib/syncthing/folder-st.go
index 7e1fe55..f25a505 100644
--- a/lib/syncthing/folder-st.go
+++ b/lib/syncthing/folder-st.go
@@ -39,7 +39,11 @@ func (f *STFolder) NewUID(suffix string) string {
if i > 15 {
i = 15
}
- return uuid.NewV1().String()[:14] + f.st.MyID[:i] + "_" + suffix
+ uuid := uuid.NewV1().String()[:14] + f.st.MyID[:i]
+ if len(suffix) > 0 {
+ uuid += "_" + suffix
+ }
+ return uuid
}
// Add a new folder
@@ -57,10 +61,8 @@ func (f *STFolder) Add(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
f.fConfig = cfg
- f.fConfig.DataCloudSync.BuilderSThgID = f.st.MyID // FIXME - should be removed after local ST config rework
-
// Update Syncthing folder
- // (expect if status is ErrorConfig)
+ // (except if status is ErrorConfig)
// TODO: add cache to avoid multiple requests on startup
if f.fConfig.Status != folder.StatusErrorConfig {
id, err := f.st.FolderChange(f.fConfig)
diff --git a/lib/syncthing/st.go b/lib/syncthing/st.go
index 99a17a1..d1ebbe6 100644
--- a/lib/syncthing/st.go
+++ b/lib/syncthing/st.go
@@ -34,16 +34,16 @@ type SyncThing struct {
STICmd *exec.Cmd
MyID string
Connected bool
+ Events *Events
// Private fields
binDir string
logsDir string
exitSTChan chan ExitChan
exitSTIChan chan ExitChan
- conf *xdsconfig.Config
client *common.HTTPClient
log *logrus.Logger
- Events *Events
+ conf *xdsconfig.Config
}
// ExitChan Channel used for process exit
@@ -134,7 +134,8 @@ func (s *SyncThing) startProc(exeName string, args []string, env []string, eChan
// Kill existing process (useful for debug ;-) )
if os.Getenv("DEBUG_MODE") != "" {
- exec.Command("bash", "-c", "pkill -9 "+exeName).Output()
+ fmt.Printf("\n!!! DEBUG_MODE set: KILL existing %s process(es) !!!\n", exeName)
+ exec.Command("bash", "-c", "ps -ax |grep "+exeName+" |grep "+s.BaseURL+" |cut -d' ' -f 1|xargs -I{} kill -9 {}").Output()
}
// When not set (or set to '.') set bin to path of xds-agent executable
@@ -227,7 +228,6 @@ func (s *SyncThing) Start() (*exec.Cmd, error) {
env := []string{
"STNODEFAULTFOLDER=1",
"STNOUPGRADE=1",
- "STNORESTART=1", // FIXME SEB remove ?
}
s.STCmd, err = s.startProc("syncthing", args, env, &s.exitSTChan)
@@ -317,7 +317,12 @@ func (s *SyncThing) Connect() error {
common.HTTPClientConfig{
URLPrefix: "/rest",
HeaderClientKeyName: "X-Syncthing-ID",
+ LogOut: s.conf.LogVerboseOut,
+ LogPrefix: "SYNCTHING: ",
+ LogLevel: common.HTTPLogLevelWarning,
})
+ s.client.SetLogLevel(s.log.Level.String())
+
if err != nil {
msg := ": " + err.Error()
if strings.Contains(err.Error(), "connection refused") {
@@ -329,11 +334,6 @@ func (s *SyncThing) Connect() error {
return fmt.Errorf("ERROR: cannot connect to Syncthing (null client)")
}
- // Redirect HTTP log into a file
- s.client.SetLogLevel(s.conf.Log.Level.String())
- s.client.LoggerPrefix = "SYNCTHING: "
- s.client.LoggerOut = s.conf.LogVerboseOut
-
s.MyID, err = s.IDGet()
if err != nil {
return fmt.Errorf("ERROR: cannot retrieve ID")
diff --git a/lib/syncthing/stfolder.go b/lib/syncthing/stfolder.go
index 70ac70a..503ba4b 100644
--- a/lib/syncthing/stfolder.go
+++ b/lib/syncthing/stfolder.go
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/iotbzh/xds-server/lib/folder"
- "github.com/syncthing/syncthing/lib/config"
+ stconfig "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
)
@@ -32,9 +32,9 @@ func (s *SyncThing) FolderLoadFromStConfig(f *[]folder.FolderConfig) error {
}
for _, stFld := range stCfg.Folders {
- cliPath := strings.TrimPrefix(stFld.RawPath, s.conf.FileConf.ShareRootDir)
+ cliPath := strings.TrimPrefix(stFld.Path, s.conf.FileConf.ShareRootDir)
if cliPath == "" {
- cliPath = stFld.RawPath
+ cliPath = stFld.Path
}
*f = append(*f, folder.FolderConfig{
ID: stFld.ID,
@@ -69,7 +69,7 @@ func (s *SyncThing) FolderChange(f folder.FolderConfig) (string, error) {
return "", err
}
- newDevice := config.DeviceConfiguration{
+ newDevice := stconfig.DeviceConfiguration{
DeviceID: devID,
Name: stClientID,
Addresses: []string{"dynamic"},
@@ -95,22 +95,22 @@ func (s *SyncThing) FolderChange(f folder.FolderConfig) (string, error) {
id = stClientID[0:15] + "_" + label
}
- folder := config.FolderConfiguration{
- ID: id,
- Label: label,
- RawPath: filepath.Join(s.conf.FileConf.ShareRootDir, f.ClientPath),
+ folder := stconfig.FolderConfiguration{
+ ID: id,
+ Label: label,
+ Path: filepath.Join(s.conf.FileConf.ShareRootDir, f.ClientPath),
}
if s.conf.FileConf.SThgConf.RescanIntervalS > 0 {
folder.RescanIntervalS = s.conf.FileConf.SThgConf.RescanIntervalS
}
- folder.Devices = append(folder.Devices, config.FolderDeviceConfiguration{
+ folder.Devices = append(folder.Devices, stconfig.FolderDeviceConfiguration{
DeviceID: newDevice.DeviceID,
})
found = false
- var fld config.FolderConfiguration
+ var fld stconfig.FolderConfiguration
for _, fld = range stCfg.Folders {
if folder.ID == fld.ID {
fld = folder
@@ -155,8 +155,8 @@ func (s *SyncThing) FolderDelete(id string) error {
}
// FolderConfigGet Returns the configuration of a specific folder
-func (s *SyncThing) FolderConfigGet(folderID string) (config.FolderConfiguration, error) {
- fc := config.FolderConfiguration{}
+func (s *SyncThing) FolderConfigGet(folderID string) (stconfig.FolderConfiguration, error) {
+ fc := stconfig.FolderConfiguration{}
if folderID == "" {
return fc, fmt.Errorf("folderID not set")
}
diff --git a/lib/webserver/server.go b/lib/webserver/server.go
index a2fdf6f..85a2c40 100644
--- a/lib/webserver/server.go
+++ b/lib/webserver/server.go
@@ -30,6 +30,7 @@ type Server struct {
mfolders *model.Folders
sdks *crosssdk.SDKs
log *logrus.Logger
+ sillyLog bool
stop chan struct{} // signals intentional stop
}
@@ -37,7 +38,7 @@ 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) *Server {
+func New(cfg *xdsconfig.Config, mfolders *model.Folders, sdks *crosssdk.SDKs, logr *logrus.Logger, sillyLog bool) *Server {
// Setup logging for gin router
if logr.Level == logrus.DebugLevel {
@@ -66,6 +67,7 @@ func New(cfg *xdsconfig.Config, mfolders *model.Folders, sdks *crosssdk.SDKs, lo
mfolders: mfolders,
sdks: sdks,
log: logr,
+ sillyLog: sillyLog,
stop: make(chan struct{}),
}
@@ -83,7 +85,7 @@ func (s *Server) Serve() error {
s.router.Use(s.middlewareCORS())
// Sessions manager
- s.sessions = session.NewClientSessions(s.router, s.log, cookieMaxAge)
+ 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)
diff --git a/lib/xdsconfig/builderconfig.go b/lib/xdsconfig/builderconfig.go
index c64fe9c..6fc1814 100644
--- a/lib/xdsconfig/builderconfig.go
+++ b/lib/xdsconfig/builderconfig.go
@@ -28,10 +28,7 @@ func NewBuilderConfig(stID string) (BuilderConfig, error) {
return b, nil
}
-// Copy makes a real copy of BuilderConfig
-func (c *BuilderConfig) Copy(n BuilderConfig) {
- // TODO
-}
+/*** Private ***/
func getLocalIP() (string, error) {
addrs, err := net.InterfaceAddrs()
diff --git a/lib/xdsconfig/config.go b/lib/xdsconfig/config.go
index 84e0778..0fc1346 100644
--- a/lib/xdsconfig/config.go
+++ b/lib/xdsconfig/config.go
@@ -13,10 +13,12 @@ import (
// Config parameters (json format) of /config command
type Config struct {
- Version string `json:"version"`
- APIVersion string `json:"apiVersion"`
- VersionGitTag string `json:"gitTag"`
- Builder BuilderConfig `json:"builder"`
+ 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"`
// Private (un-exported fields in REST GET /config route)
Options Options `json:"-"`
@@ -55,12 +57,19 @@ func Init(cliCtx *cli.Context, log *logrus.Logger) (*Config, error) {
dfltSTHomeDir = resDir
}
+ uuid, err := ServerIDGet()
+ if err != nil {
+ return nil, err
+ }
+
// Define default configuration
c := Config{
- Version: cliCtx.App.Metadata["version"].(string),
- APIVersion: DefaultAPIVersion,
- VersionGitTag: cliCtx.App.Metadata["git-tag"].(string),
- Builder: BuilderConfig{},
+ 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},
Options: Options{
ConfigFile: cliCtx.GlobalString("config"),
@@ -79,6 +88,8 @@ func Init(cliCtx *cli.Context, log *logrus.Logger) (*Config, error) {
Log: log,
}
+ c.Log.Infoln("Server UUID: ", uuid)
+
// config file settings overwrite default config
err = readGlobalConfig(&c, c.Options.ConfigFile)
if err != nil {
@@ -121,8 +132,9 @@ func Init(cliCtx *cli.Context, log *logrus.Logger) (*Config, error) {
return nil, fmt.Errorf("Cannot create logs dir: %v", err)
}
}
- c.Log.Infoln("Logs file: ", c.Options.LogFile)
- c.Log.Infoln("Logs directory: ", c.FileConf.LogsDir)
+
+ c.Log.Infoln("Logs file: ", c.Options.LogFile)
+ c.Log.Infoln("Logs directory: ", c.FileConf.LogsDir)
return &c, nil
}
diff --git a/lib/xdsconfig/data.go b/lib/xdsconfig/data.go
new file mode 100644
index 0000000..65e0fc6
--- /dev/null
+++ b/lib/xdsconfig/data.go
@@ -0,0 +1,87 @@
+package xdsconfig
+
+import (
+ "encoding/xml"
+ "fmt"
+ "os"
+
+ common "github.com/iotbzh/xds-common/golib"
+ uuid "github.com/satori/go.uuid"
+ "github.com/syncthing/syncthing/lib/sync"
+)
+
+// xmlServerData contains persistent data stored/loaded by server
+type xmlServerData struct {
+ XMLName xml.Name `xml:"XDS-Server"`
+ Version string `xml:"version,attr"`
+ Data ServerData `xml:"server-data"`
+}
+
+type ServerData struct {
+ ID string `xml:"id"`
+}
+
+var sdMutex = sync.NewMutex()
+
+// ServerIDGet
+func ServerIDGet() (string, error) {
+ var f string
+ var err error
+
+ d := ServerData{}
+ if f, err = ServerDataFilenameGet(); err != nil {
+ return "", err
+ }
+ if err = serverDataRead(f, &d); err != nil || d.ID == "" {
+ // Create a new uuid when not found
+ d.ID = uuid.NewV1().String()
+ if err := serverDataWrite(f, d); err != nil {
+ return "", err
+ }
+ }
+ return d.ID, nil
+}
+
+// serverDataRead reads data saved on disk
+func serverDataRead(file string, data *ServerData) error {
+ if !common.Exists(file) {
+ return fmt.Errorf("No folder config file found (%s)", file)
+ }
+
+ sdMutex.Lock()
+ defer sdMutex.Unlock()
+
+ fd, err := os.Open(file)
+ defer fd.Close()
+ if err != nil {
+ return err
+ }
+
+ xsd := xmlServerData{}
+ err = xml.NewDecoder(fd).Decode(&xsd)
+ if err == nil {
+ *data = xsd.Data
+ }
+ return err
+}
+
+// serverDataWrite writes persistant data to disk
+func serverDataWrite(file string, data ServerData) error {
+ sdMutex.Lock()
+ defer sdMutex.Unlock()
+
+ fd, err := os.OpenFile(file, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+ defer fd.Close()
+ if err != nil {
+ return err
+ }
+
+ xsd := &xmlServerData{
+ Version: "1",
+ Data: data,
+ }
+
+ enc := xml.NewEncoder(fd)
+ enc.Indent("", " ")
+ return enc.Encode(xsd)
+}
diff --git a/lib/xdsconfig/fileconfig.go b/lib/xdsconfig/fileconfig.go
index 2651caf..dafb034 100644
--- a/lib/xdsconfig/fileconfig.go
+++ b/lib/xdsconfig/fileconfig.go
@@ -16,6 +16,8 @@ const (
ConfigDir = ".xds-server"
// GlobalConfigFilename Global config filename
GlobalConfigFilename = "config.json"
+ // ServerDataFilename Server data filename
+ ServerDataFilename = "server-data.xml"
// FoldersConfigFilename Folders config filename
FoldersConfigFilename = "server-config_folders.xml"
)
@@ -82,7 +84,7 @@ func readGlobalConfig(c *Config, confFile string) error {
// No config file found
return nil
}
- c.Log.Infof("Use config file: %s", *cFile)
+ c.Log.Infof("Use config file: %s", *cFile)
// TODO move on viper package to support comments in JSON and also
// bind with flags (command line options)
@@ -146,11 +148,20 @@ func readGlobalConfig(c *Config, confFile string) error {
return nil
}
-// FoldersConfigFilenameGet
-func FoldersConfigFilenameGet() (string, error) {
+func configFilenameGet(cfgFile string) (string, error) {
usr, err := user.Current()
if err != nil {
return "", err
}
- return path.Join(usr.HomeDir, ConfigDir, FoldersConfigFilename), nil
+ return path.Join(usr.HomeDir, ConfigDir, cfgFile), nil
+}
+
+// FoldersConfigFilenameGet
+func FoldersConfigFilenameGet() (string, error) {
+ return configFilenameGet(FoldersConfigFilename)
+}
+
+// ServerDataFilenameGet
+func ServerDataFilenameGet() (string, error) {
+ return configFilenameGet(ServerDataFilename)
}